设置Client and World
# localhost为当前主机,2000为端口
client = carla.Client('localhost', 2000)
# 最长连接时间,若2s后仍未连接上则自动退出
client.set_timeout(2.0)
# 加载想要的地图——‘Town01’在路径Unreal/CarlaUE4/Content/Carla/Maps中
client.load_world('Town01')
# 获取仿真世界
world = client.get_world
# 改变天气
weather = carla.WeatherParameters(cloudiness=10.0, precipitation=10.0, fog_density=10.0)
world.set_weather(weather)
或许制作海洋地图,可以直接把UE4中搭建好的海洋场景复制到文件夹Unreal/CarlaUE4/Content/Carla/Maps中(到时候看看不搞xdor文件能不能做)
Pawn Actor生成与销毁
首先找到actor的蓝图:
# 拿到这个世界所有物体的蓝图
blueprint_library = world.get_blueprint_library()
# 从vehicle蓝图库中找到vehicle蓝图,后面‘’内的为vehicle文件名
ego_vehicle_bp = blueprint_library.find('vehicle.mercedes-benz.coupe')
直接加入UE4中构建好的USV蓝图,或许可以参照Carla官方文件中“Add a new vehicle”中的第9步,这里把ChatGPT的翻译粘贴如下:
接着寻找可生成vehicle的点并生成:
# 找到所有可以作为初始点的位置并随机选择一个
transform = random.choice(world.get_map().get_spawn_points())
# 在这个位置生成小车
# 未必是主角小车(但事实上ego_vehicle就已经点名是了),是否可玩家操控取决于生成角色时所使用的蓝图以及后续的代码逻辑
ego_vehicle = world.spawn_actor(ego_vehicle_bp, transform)
如果使用海洋地图的话,不知道world.get_map().get_spawn_points()函数还能不能使用,若不能,或许可以将transform直接编辑为生成点坐标
最后一定要记得注销你的vehicle!
# 如果注销单个Actor
ego_vehicle.destroy()
# 如果你有多个Actor 存在list里,想一起销毁。
client.apply_batch([carla.command.DestroyActor(x) for x in actor_list])
观察者(spectator)放置
# 获取spectator与小车位置(借此确定spectator位置)
spectator = world.get_spectator()
transform = ego_vehicle.get_transform()
# 解析:z=20让观察者位置在小车之上,pitch=-90让观察者的俯仰角为-90度,实现俯视
spectator.set_transform(carla.Transform(transform.location + carla.Location(z=20), carla.Rotation(pitch=-90)))
同步模式的设置与实现
Carla仿真有两种模式:异步模式(默认)和同步模式。前者会尽可能快的刷新,导致刷新时间间隔是不定的,在采集数据时会出现掉帧现象(数据存储所用时大于刷新时间,上一帧的数据还没存储好就刷新到下一帧了),而同步模式则可以设定刷新频率(一个恒定的值,或使用queue等待某任务完成后刷新)。
异步模式设置:
settings = world.get_settings()
# None则为设置为异步模式
settings.fixed_delta_seconds = None
world.apply_settings(settings)
恒定刷新频率的同步模式设置:
settings = world.get_settings()
# 恒定刷新频率的同步模式:每0.05s刷新一次,每秒刷新20次
settings.fixed_delta_seconds = 0.05
world.apply_settings(settings)
等待任务完成再刷新的同步模式设置:
# 定义callback function函数
def sensor_callback(sensor_data, sensor_queue, sensor_name):
if 'lidar' in sensor_name:
sensor_data.save_to_disk(os.path.join('../outputs/output_synchronized', '%06d.ply' % sensor_data.frame))
if 'camera' in sensor_name:
sensor_data.save_to_disk(os.path.join('../outputs/output_synchronized', '%06d.png' % sensor_data.frame))
# 将传感器数据的帧数和传感器名称放入queue
sensor_queue.put((sensor_data.frame, sensor_name))
settings = world.get_settings()
# 初步设置等待client的同步模式(后面queue为进一步等待client完全完成全部任务的设置)
settings.synchronous_mode = True
world.apply_settings(settings)
# 选择camera蓝图和位置(详见sensor模块)
camera = world.spawn_actor(blueprint, transform)
# 创建一个队列,用于调用sensor_callback函数时使用
sensor_queue = queue.Queue()
camera.listen(lambda image: sensor_callback(image, sensor_queue, "camera"))
while True:
# world.tick让仿真更新一次,即从正文第一行开始重新执行,设置,camera拍照
world.tick()
# queue.get()用于从队列中获取数据
# 'block=True'让表示如果队列中没有数据,程序会被阻塞,直到有数据可供获取
# 当队列中有数据时,sensor_queue.get() 方法会返回该数据,并将其从队列中移除
data = sensor_queue.get(block=True)
Carla只支持单客户同步,那对于其它异步模式的客户,可以参考如下代码:
# 这两句应该是放在while True的循环里
# world.wait_for_tick()代表等待模拟器进行下一步模拟,并返回该步的快照
# 故此句为异步模式的客户通过world.wait_for_tick()等待server更新
world_snapshot = world.wait_for_tick()
# world.on_tick代表每个仿真步骤完成后触发callback函数,callback函数中可以写从snapshot中提取信息并进行处理
world.on_tick(callback)
最后要将世界设置回异步模式!(参考文章的代码是把这一段放在了finally里面,大抵是更保险)
settings = world.get_settings()
settings.synchronous_mode = False
settings.fixed_delta_seconds = None
world.apply_settings(settings)