3、Carla基础操作:世界与蓝图、生成车辆与行人、控制传感器、获取仿真数据、简单交通流模拟
好,咱们正式开始动手操作Carla。这一章是基础中的基础,但也是整个仿真流程的骨架。说白了,你后面所有花里胡哨的标注、自动标注、模型训练,都得建立在这些操作之上。我个人习惯,每次开新项目,都会先把这一套流程跑一遍,确保环境没问题。
3.1 世界与蓝图:Carla的「上帝视角」
Carla里的「世界」(World)是什么?你可以把它理解成整个仿真宇宙的容器。它管理着地图、天气、时间,还有所有你生成出来的东西。而「蓝图」(Blueprint)呢,就是物体的「出生证明」——它定义了车辆长什么样、行人穿什么衣服、传感器有什么参数。
先看怎么拿到这个世界:
import carla
# 连接Carla服务器
client = carla.Client('localhost', 2000)
client.set_timeout(10.0)
# 获取当前世界
world = client.get_world()
嗯,这里要注意。如果你同时开了多个Carla窗口,get_world()拿到的可能是你最后激活的那个。我在项目中遇到过,调试半天发现车怎么不动了,结果连错了世界。
蓝图库怎么用?很简单:
# 获取所有可用的蓝图
blueprint_library = world.get_blueprint_library()
# 筛选出所有车辆蓝图
vehicle_bps = blueprint_library.filter('vehicle.*')
# 挑一辆你喜欢的车
bp = blueprint_library.find('vehicle.tesla.model3')
你想想看,这个蓝图库其实就是一个巨大的超市货架。你想要什么车、什么颜色的行人、什么型号的摄像头,都能在这里找到。我个人建议,刚开始做实验时,用filter方法缩小范围,别一次性把几千个蓝图全打印出来,眼睛会花。
3.2 生成车辆与行人:让世界活起来
有了蓝图,下一步就是「生」出来。Carla里生成物体用的是spawn_actor方法。你需要指定蓝图和位置。
import random
# 获取一个随机生成点
spawn_points = world.get_map().get_spawn_points()
spawn_point = random.choice(spawn_points)
# 生成车辆
vehicle = world.spawn_actor(bp, spawn_point)
这里有个坑,我踩过好几次。Carla的生成点(Spawn Point)是地图预定义的,不是随便一个坐标都能用。如果你用自定义坐标,很可能车直接卡在地底下或者飞到天上。我曾经为了省事,直接写死坐标,结果车悬在半空中,轮子还在转,看着特别诡异。
生成行人稍微复杂一点:
# 获取行人蓝图
walker_bp = random.choice(blueprint_library.filter('walker.pedestrian.*'))
# 行人需要特殊的位置(通常在人行道上)
walker_spawn_point = carla.Transform()
walker_spawn_point.location = world.get_random_location_from_navigation()
# 生成行人
walker = world.spawn_actor(walker_bp, walker_spawn_point)
为什么行人要用get_random_location_from_navigation()?因为行人不能站在马路中间啊。这个方法会自动帮你找一个合法的步行区域。说白了,Carla在底层已经帮你做了很多合理性检查。
3.3 控制传感器:给车装上「眼睛」
传感器是自动驾驶的感官。Carla支持摄像头、激光雷达、毫米波雷达、GPS、IMU等等。我个人最常用的是RGB摄像头和激光雷达。
先看摄像头怎么装:
# 获取摄像头蓝图
camera_bp = blueprint_library.find('sensor.camera.rgb')
camera_bp.set_attribute('image_size_x', '800')
camera_bp.set_attribute('image_size_y', '600')
camera_bp.set_attribute('fov', '90')
# 把摄像头装在车顶前方
camera_transform = carla.Transform(carla.Location(x=1.5, z=2.4))
camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle)
注意这个attach_to参数。它把传感器「绑定」到了车上。车动,传感器跟着动。你想想看,如果你把摄像头固定在世界坐标里,车一开出去,摄像头就拍不到车了,那还仿什么真。
激光雷达的配置更讲究:
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast')
lidar_bp.set_attribute('channels', '64')
lidar_bp.set_attribute('range', '100')
lidar_bp.set_attribute('points_per_second', '1000000')
lidar_transform = carla.Transform(carla.Location(x=0.0, z=2.4))
lidar = world.spawn_actor(lidar_bp, lidar_transform, attach_to=vehicle)
这里channels是线数,64线比16线稠密得多,但计算量也大。我在项目中遇到过,为了追求点云密度,把points_per_second设得太高,结果仿真帧率直接掉到个位数。嗯,这里要平衡。
3.4 获取仿真数据:把「看到的」存下来
传感器装好了,怎么拿数据?Carla用的是回调函数机制。传感器一有数据,就会调用你注册的函数。
import numpy as np
# 定义回调函数
def process_camera_data(image):
# 把图像数据转成numpy数组
array = np.frombuffer(image.raw_data, dtype=np.uint8)
array = array.reshape((image.height, image.width, 4))
# 去掉alpha通道
array = array[:, :, :3]
# 保存图片
image.save_to_disk(f'output/{image.frame:06d}.png')
# 注册回调
camera.listen(process_camera_data)
激光雷达的数据处理类似:
def process_lidar_data(point_cloud):
# 点云数据格式:x, y, z, intensity
points = np.frombuffer(point_cloud.raw_data, dtype=np.float32)
points = points.reshape(-1, 4)
# 保存为npy文件
np.save(f'output/lidar_{point_cloud.frame:06d}.npy', points)
lidar.listen(process_lidar_data)
这里有个细节。Carla的图像数据是BGRA格式的,不是常见的RGB。我第一次用的时候,直接显示出来,颜色全不对,折腾了半天才发现是通道顺序的问题。你如果要用OpenCV显示,记得用cv2.COLOR_BGRA2BGR转换一下。
3.5 简单交通流模拟:让车自己跑起来
单辆车静止不动,那叫摆拍。真正的仿真需要交通流。Carla提供了Traffic Manager,专门管理车辆的行为。
# 创建Traffic Manager
traffic_manager = client.get_trafficmanager(8000)
# 设置全局速度限制(km/h)
traffic_manager.global_percentage_speed_difference(10.0)
# 让车辆自动驾驶
vehicle.set_autopilot(True, traffic_manager)
就这么简单?对,就这么简单。Traffic Manager会自动控制车辆的油门、刹车、转向,还会遵守红绿灯、避让行人。你想想看,如果你要自己写一套交通流逻辑,那得写到猴年马月去。
批量生成车辆并开启自动驾驶:
# 生成10辆车
for i in range(10):
bp = random.choice(vehicle_bps)
spawn_point = random.choice(spawn_points)
vehicle = world.spawn_actor(bp, spawn_point)
vehicle.set_autopilot(True, traffic_manager)
这里要注意,生成点不能重复。如果两个车生成在同一个位置,它们会「融合」在一起,物理引擎会爆炸。我遇到过,两辆车重叠后,直接弹射到天上去了,画面特别喜感。
行人的交通流稍微不同,需要手动控制:
# 让行人沿着导航路径行走
walker_controller = world.spawn_actor(
blueprint_library.find('controller.ai.walker'),
carla.Transform(),
attach_to=walker
)
walker_controller.start()
walker_controller.go_to_location(
world.get_random_location_from_navigation()
)
行人的AI控制器和车辆的Traffic Manager是两套系统。我刚开始以为行人也能用set_autopilot,结果报错了。嗯,Carla里行人和车辆的控制逻辑是分开的,别搞混。
set_percentage_speed_difference控制速度变化范围,set_global_distance_to_leading_vehicle控制跟车距离。我一般会把跟车距离设小一点,这样车流更密集,更接近城市路况。
好了,这一章的内容就这些。你掌握了世界与蓝图、生成物体、控制传感器、获取数据、交通流模拟,基本上Carla的核心操作你就都摸到了。下一章我们会把这些基础组合起来,做一个完整的仿真数据采集流水线。