3、Carla世界基础:加载地图、设置天气、控制时间步长、理解世界坐标系
好,我们正式开始动手操作Carla了。这一章,我带你搞定仿真世界的底层配置。说白了,就是告诉Carla:「我要在什么样的路上跑,天是什么颜色,时间怎么流动,以及我的车到底在哪儿。」
这些基础操作,你后面每个测试用例都会用到。我刚开始用Carla的时候,就是在这几个地方踩了不少坑。嗯,咱们一个一个来。
3.1 加载地图:选对战场
Carla自带了好几个地图。每个地图的复杂度、道路类型、场景元素都不一样。我个人习惯把地图分成三类:
| 地图名称 | 特点 | 适用场景 |
|---|---|---|
| Town01 | 基础网格状道路,简单直路+十字路口 | 新手入门、基础功能验证 |
| Town03 | 有环岛、有高架、有复杂路口 | 中等复杂度测试、变道场景 |
| Town05 | 高速公路+城市混合,车道多、车流大 | 高速场景、多车交互、回归测试 |
| Town10HD | 高清城市,建筑细节丰富,有行人 | 视觉感知测试、行人避让 |
加载地图的代码很简单,但有个细节我提醒你:
import carla
# 连接Carla服务端
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
# 加载地图
world = client.load_world('Town03')
# 或者重新加载当前地图(重置所有状态)
client.reload_world()
load_world 来重置场景,结果发现所有车辆、传感器状态都丢了。后来才搞清楚:load_world 是彻底换地图,reload_world 是重置当前地图。这两个别搞混了。
你想想看,如果你的回归测试脚本里,每次跑完用例都 load_world 一次,那地图加载时间就够你喝一壶的。我建议:只在切换地图时用 load_world,平时重置用 reload_world。
3.2 设置天气:别让老天爷捣乱
天气对自动驾驶感知的影响,不用我多说吧?大雾、雨天、黄昏,这些场景必须覆盖。Carla的天气系统很灵活,你可以直接选预设,也可以自己调参数。
先看预设天气:
# 获取天气预设列表
weather_presets = [
carla.WeatherParameters.ClearNoon,
carla.WeatherParameters.CloudyNoon,
carla.WeatherParameters.WetNoon,
carla.WeatherParameters.WetCloudyNoon,
carla.WeatherParameters.MidRainyNoon,
carla.WeatherParameters.HardRainNoon,
carla.WeatherParameters.SoftRainNoon,
carla.WeatherParameters.ClearSunset,
]
# 设置天气
world.set_weather(carla.WeatherParameters.ClearNoon)
但说实话,预设天气有时候不够用。比如你想模拟「刚下过雨但地面还没干透」这种状态,就得自己调参数:
# 自定义天气
weather = carla.WeatherParameters(
cloudiness=30.0, # 云量 0-100
precipitation=0.0, # 降水量 0-100
precipitation_deposits=60.0, # 地面积水 0-100
wind_intensity=10.0, # 风速 0-100
sun_altitude_angle=45.0 # 太阳高度角 -90到90
)
world.set_weather(weather)
weather_config.yaml 里定义好「晴天」「小雨」「大雾」三档。这样测试用例只需要读配置,不用改代码。
为什么会特别关注地面积水?因为积水会导致车道线反光、反射倒影,很多视觉算法在这上面翻过车。我在做AEB测试时,就遇到过因为积水反射导致误制动的情况。嗯,这个坑你得记住。
3.3 控制时间步长:让仿真跑得稳
时间步长,说白了就是仿真世界「滴答」一下,现实世界过了多久。这个参数直接影响你的仿真精度和速度。
Carla有两种模式:
- 同步模式(Synchronous Mode):你控制每一步。你调用
world.tick(),仿真才前进一个步长。适合需要精确控制的场景,比如回归测试。 - 异步模式(Asynchronous Mode):Carla自己跑,你只管读数据。适合可视化演示,但数据时序不可控。
我个人强烈建议:做仿真测试,必须用同步模式。为什么?
# 设置同步模式
settings = world.get_settings()
settings.synchronous_mode = True
settings.fixed_delta_seconds = 0.05 # 50ms一个步长,即20FPS
world.apply_settings(settings)
# 手动推进仿真
for i in range(100):
world.tick()
# 在这里读取传感器数据、做决策
# 每一步都是确定的,可复现的
核心要点: 同步模式下,同样的代码、同样的初始条件,跑出来的结果完全一样。这是回归验证的基石。你想想看,如果每次跑结果都不一样,你怎么判断这次改代码是修了bug还是引入了新bug?
固定步长选多少?我一般这么选:
- 0.05秒(20FPS):大多数场景够用,计算量适中
- 0.025秒(40FPS):需要高精度碰撞检测时用,比如AEB测试
- 0.1秒(10FPS):只做路径规划验证,不关心细节时用
world.tick(),仿真就卡死了。我曾经在调试一个多车协同场景时,忘了给其中一辆车发tick指令,结果整个仿真卡了半小时。嗯,从那以后我都在脚本开头加一个超时保护。
3.4 理解世界坐标系:别让车跑偏了
Carla用的是UE4的坐标系,跟一般的自动驾驶坐标系有点不一样。你得记住这个:
- X轴:向前(车头方向)
- Y轴:向右
- Z轴:向上
单位是厘米?不,是米。Carla里所有距离单位都是米。
获取车辆位置和朝向:
# 获取车辆的位置和朝向
vehicle = world.get_actors().filter('vehicle.*')[0]
transform = vehicle.get_transform()
location = transform.location # carla.Location(x, y, z)
rotation = transform.rotation # carla.Rotation(pitch, yaw, roll)
print(f"位置: x={location.x:.2f}, y={location.y:.2f}, z={location.z:.2f}")
print(f"朝向: yaw={rotation.yaw:.2f}°")
这里有个容易搞混的地方:yaw角。Carla里yaw=0表示正X方向,逆时针为正。也就是说:
- yaw=0:朝正X
- yaw=90:朝正Y(向右)
- yaw=-90:朝负Y(向左)
- yaw=180:朝负X(向后)
我在做路径规划测试时,经常需要把全局路径点转换成车辆坐标系。这时候就要用到 get_transform() 的逆矩阵:
# 将世界坐标点转换到车辆坐标系
world_point = carla.Location(x=100, y=50, z=0)
vehicle_transform = vehicle.get_transform()
# 获取车辆坐标系下的相对位置
relative_point = vehicle_transform.inverse_transform(world_point)
print(f"相对位置: x={relative_point.x:.2f}, y={relative_point.y:.2f}")
3.5 实战组合:一个完整的初始化流程
好了,把上面这些串起来,就是一个标准的仿真初始化流程。我一般这样写:
def init_simulation(map_name='Town03', weather_preset='ClearNoon', dt=0.05):
# 1. 连接并加载地图
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
world = client.load_world(map_name)
# 2. 设置天气
weather_map = {
'ClearNoon': carla.WeatherParameters.ClearNoon,
'RainyNoon': carla.WeatherParameters.MidRainyNoon,
'Sunset': carla.WeatherParameters.ClearSunset,
}
world.set_weather(weather_map.get(weather_preset, carla.WeatherParameters.ClearNoon))
# 3. 设置同步模式和时间步长
settings = world.get_settings()
settings.synchronous_mode = True
settings.fixed_delta_seconds = dt
world.apply_settings(settings)
# 4. 等待一帧,让世界稳定下来
world.tick()
return client, world
这个函数,我每个测试脚本都会复用。你把它放在一个 carla_utils.py 里,后面写测试用例就省事多了。
嗯,这一章的内容就这些。你先把地图加载、天气设置、时间步长、坐标系这几个基础打牢。下一章,我们开始往世界里放车、放传感器,真正跑起来。