3、CARLA基础架构:服务器-客户端架构解析、世界(World)与蓝图(Blueprint)概念、时间步长与帧率控制
好,咱们正式开始啃CARLA的底层架构。说实话,很多新手一上来就急着跑示例代码,结果连服务器和客户端谁先启动都搞不清楚,最后报一堆连接错误。我个人习惯是,先花半小时把架构理清楚,后面写代码会顺畅很多。
3.1 服务器-客户端架构:谁在干活?
CARLA采用的是典型的服务器-客户端(Server-Client)架构。说白了,就是一台机器负责「跑世界」,另一台(或同一个进程)负责「下指令」。
- 服务器(CARLA Server):负责物理仿真、渲染、传感器数据生成。它是个独立的可执行文件,比如
./CarlaUE4.sh或CarlaUE4.exe。 - 客户端(Client):通过Python API连接服务器,发送指令——比如生成一辆车、设置天气、读取传感器数据。
我在项目中遇到过一个问题:同时开了两个客户端连接同一个服务器,结果车辆控制互相打架。嗯,这里要注意——一个服务器可以接受多个客户端,但共享同一个世界状态。如果你需要多进程控制,得自己做好锁机制。
核心要点:服务器是「大脑」,客户端是「手」。大脑不启动,手再灵活也没用。
3.2 世界(World)与蓝图(Blueprint)
这两个概念,你想想看,其实特别像游戏里的「地图」和「角色模板」。
3.2.1 世界(World)
World就是整个仿真环境的容器。它包含了:
- 所有静态物体(建筑、道路、路灯)
- 所有动态物体(车辆、行人、自行车)
- 天气系统、光照、时间
- 传感器管理器
获取World的方式很简单:
import carla
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
world = client.get_world() # 拿到当前世界
我曾经犯过一个低级错误:在客户端连接后直接调用 world.get_actors(),结果返回空列表。后来才发现——服务器刚启动时,世界里啥也没有,你得自己往里面放东西。
3.2.2 蓝图(Blueprint)
Blueprint是CARLA里所有可生成物体的「模板」。它定义了:
- 模型(比如奥迪A2、特斯拉Model 3)
- 颜色、属性(比如车门数量、是否可碰撞)
- 传感器类型(比如RGB相机、激光雷达)
获取所有蓝图:
blueprint_library = world.get_blueprint_library()
# 筛选出所有车辆蓝图
vehicle_bps = blueprint_library.filter('vehicle.*')
我个人习惯用 filter 而不是 find,因为 filter 支持通配符,更灵活。比如你想找所有带「自行车」字样的蓝图:
bike_bps = blueprint_library.filter('*bike*')
小技巧:打印蓝图的所有属性,用 print(bp.get_tags()) 和 print(bp.get_attribute('color').recommended_values)。这样你就知道这辆车能刷成什么颜色了。
3.3 时间步长与帧率控制
这是仿真里最容易出坑的地方。你想想看,如果仿真跑得忽快忽慢,传感器数据还能对齐吗?
3.3.1 固定时间步长 vs 可变时间步长
CARLA默认是可变时间步长——服务器尽力跑,每帧时间不固定。这适合做演示,但不适合做算法开发。
我建议做自动驾驶算法测试时,一定要用固定时间步长:
settings = world.get_settings()
settings.fixed_delta_seconds = 0.05 # 20 FPS
settings.synchronous_mode = True # 同步模式
world.apply_settings(settings)
这里 fixed_delta_seconds = 0.05 意味着每帧固定20毫秒,也就是20 FPS。为什么是20?因为大多数传感器(比如相机)在20 FPS下数据量适中,既不会丢帧,也不会撑爆内存。
3.3.2 同步模式 vs 异步模式
同步模式(Synchronous Mode)下,客户端必须主动调用 world.tick() 才能推进一帧。异步模式(默认)下,服务器自己跑,客户端只管读数据。
我曾经在项目里用异步模式做多传感器融合,结果发现相机和激光雷达的时间戳差了整整两帧——因为服务器在异步模式下不会等你。从那以后,只要涉及传感器数据对齐,我必开同步模式。
警告:开启同步模式后,如果客户端忘记调用 world.tick(),服务器会一直卡住。建议在代码里加一个超时保护,比如用 timeout = 10 的循环检测。
3.4 知识体系结构图
下面这张图,是我自己总结的CARLA基础架构关系。你看一眼,后面所有章节都会围绕这个结构展开。
3.5 避坑指南:我踩过的三个坑
- 端口冲突:默认端口2000,如果你同时跑多个CARLA实例,记得用
carla.Client('localhost', 2001)指定不同端口。 - 蓝图属性未设置:生成车辆前,一定要设置
bp.set_attribute('role_name', 'hero'),否则有些传感器会找不到目标车辆。 - 时间步长与传感器不匹配:如果你用100 FPS的激光雷达,但固定步长设成0.05秒(20 FPS),那激光雷达数据会重复。记得让传感器频率 ≤ 仿真帧率。
我的习惯:每次启动仿真前,先打印一下当前设置:print(world.get_settings())。确认时间步长和同步模式符合预期,再开始跑算法。这一步能省下大量调试时间。
好了,基础架构就讲到这里。记住一句话:服务器管世界,客户端管指令,蓝图管模板,时间步长管节奏。这四个东西搞明白了,后面写代码就是搭积木的事。