自动驾驶仿真:carla同步模式

自动驾驶仿真:carla同步模式

附赠自动驾驶最全的学习资料和量产经验:链接

在上一个blog里,我们成功地在仿真世界造了一辆奔驰小轿车,设置了自动驾驶模式,并安置了相机与lidar不停地收集数据储存在硬盘里。但是我们发现,储存的相机照片有着严重的掉帧现象,这是为什么呢?

一句话简单总结原因,因为咱们的仿真server默认为异步模式,它会尽可能快地进行仿真,根本不管客户是否跟上了它的步伐。接下来我会更详细地介绍这个原因,并提出解决方案。

仿真世界里的时间步长

在simulation里的时间与真实世界是不同的,simulation里没有“一秒”的概念,只有“一个time-step"的概念。这个time-step相当于仿真世界进行了一次更新(比如小车们又往前挪了一小步,天气变阴了一丢丢),它在真实世界里的时间可能只有几毫秒。

仿真世界里的这个time-step其实有两种,一种是Variable time-step, 另一种是Fixed time-step.

  • Variable time-step. 顾名思义,仿真每次步长所需要的真实时间是不一定的,可能这一步用了3ms, 下一步用了5ms, 但是它会竭尽所能地快速运行。这是仿真默认的模式:

    settings = world.get_settings()
    settings.fixed_delta_seconds = None # Set a variable time-step
    world.apply_settings(settings)

  • Fixed time-step. 在这种时间步长设置下,每次time-step所消耗的时间是固定的,比如永远是5ms. 设置代码如下:

    settings = world.get_settings()
    settings.fixed_delta_seconds = 0.05 #20 fps, 5ms
    world.apply_settings(settings)

同步模式

看到这里,我相信各位小伙伴们已经猜到了,carla simulation默认模式为异步模式+variable time-step, 而同步模式则对应fixed time-step.

在异步模式下, server会自个跑自个的,client需要跟随它的脚步,如果client过慢,可能导致server跑了三次,client才跑完一次, 这就是为什么咱们照相机储存的照片会掉帧的原因。

而在同步模式下,simulation会等待客户完成手头的工作后,再进行下一次更新。假设simulation每次更新只需要固定的5ms,但我们客户端储存照片需要10ms, 那么simulation就会等照片储存完才进行下一次更新,也就是说,一个真正cycle耗时10ms(simulation更新与照片储存是同时开始进行的)。设置代码关键部分如下:

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))
    sensor_queue.put((sensor_data.frame, sensor_name))

settings = world.get_settings()
settings.synchronous_mode = True
world.apply_settings(settings)

camera = world.spawn_actor(blueprint, transform)
sensor_queue = queue.Queue()
camera.listen(lambda image: sensor_callback(image, sensor_queue, "camera"))

while True:
    world.tick()
    data = sensor_queue.get(block=True)

这段代码首先注意到的是 world.tick() 这个函数。它只出现于同步模式,意思是让simulation更新一次。然后我们还会发现这里用了python自带的Queue, queue.get有一个功效,就是在它把列队里所有内容都提取出来之前,会阻止任何其他进程越过自己这一步,相当于一个blocker。如果没有这个queue,你会发现仿真虽然设置成了同步模式,还是照样会自个跑自个的。

所以你可以这样理解,settings.synchronous_mode = True 让仿真的更新要通过这个client来唤醒,但这并不能保证它会等该client其他进程运行完,必须要再加一个queue来阻挡一下它,逼迫它等着该客户其他线程搞定。也就是说,启动同步模式,让你的server学会等待客户的必要条件有三个:

  1. settings.synchronous_mode = True

  2. world.tick()

  3. Thread Blocker(such as Queue)

当你将上述代码加到我们上一次的程序里,会发现,照片是不掉帧了,但是小车一动也不动了。这里是因为同步模式下汽车要使用autopilot必须依附于开启同步模式的traffic manager.至于这个traffic manager是何方神圣,咱们下期会详细讲述,现在你只需要如何操作:

traffic_manager = client.get_trafficmanager(8000)       traffic_manager.set_synchronous_mode(True)      ego_vehicle = world.spawn_actor(ego_vehicle_bp, transform)      ego_vehicle.set_autopilot(True, 8000)

注意事项

  1. 目前carla只支持单客户同步,也就是说,如果你有N个python scripts(N个client), 只能在其中一个client里设置同步模式, 而其他client只能异步模式. 这些处于异步模式的客户首先通过world.wait_for_tick() 等待server 更新,一旦更新了它们会立刻通过world.on_tick 里的callback 来提取这个更新的wordsnapshot里面的信息(比如timestamp, 这个on_tick()我会在以后的分享里详细说明举例,现在暂且用不到).

    Wait for the next tick and retrieve the snapshot of the tick.

    world_snapshot = world.wait_for_tick()

    Register a callback to get called every time we receive a new snapshot.

    world.on_tick(callback)

2. 在你设置了同步模式的client完成了它的任务准备停止/销毁时,千万别忘了将世界设置回异步模式,否则server会因为找不到它的同步客户而卡死。

try:
  .......
finally:
  settings = world.get_settings()
  settings.synchronous_mode = False
  settings.fixed_delta_seconds = None
  world.apply_settings(settings)

完整代码:

完整代码已经上线啦!

总结

同步/异步模式因为涉及到多线程的问题,设置和使用的时候要格外小心,一般设置同步模式的客户主要是用来做数据储存与采集的。在下一期里,我将会讲到如何通过神秘的Traffic Manager, 让街道充满各种不同行为模式的汽车,为你的无人汽车模拟真实的交通环境

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值