Python API入门:连接Carla客户端、获取世界对象、加载地图、设置天气

好,咱们正式开始动手了。上一章我们把Carla环境搭好,现在该写代码了。说实话,我第一次接触Carla的Python API时,觉得这东西挺绕的——又是客户端又是世界,还有一堆回调。但用顺手了你会发现,它的设计其实很清晰。

这一章,我们只做四件事:连上Carla、拿到世界、换个地图、调个天气。别小看这四步,它们是所有仿真脚本的基石。

2.1 连接Carla客户端

先说说客户端这个概念。Carla采用C/S架构,服务端就是那个CarlaUE4.exe(或者Linux下的sh脚本),客户端就是我们写的Python脚本。两者通过网络通信。

连接代码很简单:

import carla

# 创建客户端,默认地址是localhost:2000
client = carla.Client('localhost', 2000)

# 设置超时时间,单位秒
client.set_timeout(10.0)

print('客户端已连接')

这里有个坑,我一开始就踩过——超时时间。如果你不设timeout,默认是无穷大,脚本会卡死在那里。我建议至少设5秒以上,因为Carla启动后加载地图需要时间,尤其是大场景。

注意:连接前请确保Carla服务端已经启动。如果连不上,先检查端口2000是否被占用,或者Carla是否真的跑起来了。

2.2 获取世界对象

连上客户端后,下一步就是获取世界(World)。世界是Carla里最核心的对象,它管理着地图、车辆、行人、传感器、天气……几乎所有东西。

# 获取当前世界
world = client.get_world()

print(f'当前地图: {world.get_map().name}')

拿到world对象后,你可以做很多事情。比如获取所有参与者(actors)、设置天气、获取地图信息等等。我个人习惯把world保存成一个全局变量,后面所有操作都基于它。

为什么会这样设计?因为Carla把整个仿真环境抽象成了一个“世界”,你想想看,现实世界中你要改变天气、移动物体,不也是通过某种“世界规则”来操作的吗?

2.3 加载地图

Carla自带了好几个地图,比如Town01到Town07,还有最新的Town10。每个地图的布局、道路复杂度都不一样。

加载地图有两种方式:

  1. 通过地图名称加载:直接指定地图名
  2. 通过地图文件路径加载:加载自定义地图

先看第一种:

# 加载Town03地图
world = client.load_world('Town03')

# 等待地图加载完成
world.wait_for_tick()

print('地图已切换为Town03')

这里有个细节——wait_for_tick()。Carla的仿真世界是按“tick”推进的,每个tick相当于一帧。加载地图后,需要等一个tick让世界完成初始化。我曾经没加这行,结果后续操作报了一堆空指针错误。

第二种方式用于加载你自己用RoadRunner导出的地图:

# 加载自定义地图(假设文件在Carla的Content目录下)
client.load_world('/Game/Carla/Maps/MyCustomMap')
小提示:如果你不确定有哪些地图可用,可以用 client.get_available_maps() 列出所有地图。我经常用这个命令来检查新导入的地图是否被识别。

2.4 设置天气

天气系统是Carla的一大亮点。你可以控制阳光、雨、雾、云层、风速……甚至能模拟沙尘暴(虽然我没试过)。

设置天气有两种方式:

方式一:使用预设天气

Carla提供了一些预设的天气参数,比如晴天、雨天、夜晚等:

# 获取世界对象
world = client.get_world()

# 设置天气为晴天
weather = carla.WeatherParameters.ClearNoon
world.set_weather(weather)

print('天气已设置为晴天')

预设天气有这些:

预设名称 说明
ClearNoon 晴天正午
CloudyNoon 多云正午
WetNoon 雨后正午
WetCloudyNoon 雨后多云正午
MidRainyNoon 中雨正午
HardRainNoon 大雨正午
SoftRainNoon 小雨正午
ClearSunset 晴天黄昏
CloudySunset 多云黄昏
WetSunset 雨后黄昏
WetCloudySunset 雨后多云黄昏
MidRainySunset 中雨黄昏
HardRainSunset 大雨黄昏
SoftRainSunset 小雨黄昏

方式二:自定义天气参数

预设天气不够用?那就自己调参数。每个参数都有取值范围:

# 自定义天气:大雾+小雨
weather = carla.WeatherParameters(
    cloudiness=80.0,       # 云量 0-100
    precipitation=30.0,    # 降雨量 0-100
    precipitation_deposits=50.0,  # 路面积水 0-100
    wind_intensity=20.0,   # 风速 0-100
    sun_azimuth_angle=180.0,  # 太阳方位角
    sun_altitude_angle=45.0,  # 太阳高度角
    fog_density=70.0,      # 雾浓度 0-100
    fog_distance=10.0,     # 雾的可见距离
    fog_falloff=1.0,       # 雾衰减
    wetness=60.0           # 路面湿润度 0-100
)

world.set_weather(weather)
print('自定义天气已应用')

嗯,这里要注意——参数不是随便填的。比如fog_distance设得太小,你会什么都看不见。我刚开始调的时候,把雾浓度设到100,结果整个场景白茫茫一片,连车在哪都找不到。

避坑指南:我曾经在测试传感器时,因为天气设成了大雨,激光雷达的点云数据全是噪点。后来才发现是雨滴反射造成的。所以做传感器测试时,建议先用晴天,排除天气干扰。

2.5 完整示例:一条龙搞定

把上面所有步骤串起来,就是一个完整的入门脚本:

import carla
import time

def main():
    # 1. 连接客户端
    client = carla.Client('localhost', 2000)
    client.set_timeout(10.0)
    
    # 2. 加载地图
    world = client.load_world('Town03')
    world.wait_for_tick()
    
    # 3. 设置天气(自定义:多云+微风)
    weather = carla.WeatherParameters(
        cloudiness=60.0,
        precipitation=0.0,
        wind_intensity=10.0
    )
    world.set_weather(weather)
    
    # 4. 打印当前状态
    print(f'地图: {world.get_map().name}')
    print(f'天气: 云量{weather.cloudiness}%, 风速{weather.wind_intensity}')
    
    # 保持仿真运行一会儿
    for i in range(10):
        world.tick()
        time.sleep(0.1)
    
    print('仿真运行结束')

if __name__ == '__main__':
    main()

这个脚本你直接复制就能跑。它会加载Town03,设置多云天气,然后跑10个tick(大约1秒)。

2.6 本章小结

这一章我们做了四件事:

  • 连接客户端carla.Client() + set_timeout()
  • 获取世界client.get_world()client.load_world()
  • 加载地图:指定地图名,别忘了 wait_for_tick()
  • 设置天气:预设或自定义参数

说白了,这就是Carla仿真的“开机四步”。后面所有章节,都会基于这四步展开。你想想看,不管是加车、加传感器、还是做交通流,都得先有世界和地图对吧?

下一章,我们开始往世界里放车——生成车辆、控制车辆、获取车辆状态。那才是真正好玩的部分。