(八) carla中同步模式设置

(八) carla中同步模式设置

前言

由于前一段时间在 c a r l a carla carla 中采集数据时发现:采集的图片的帧数是跳帧的,不连续,通过 p y g a m e pygame pygame 也可以看到 c a r l a carla carla 服务端和客户端的频率也是不一样的。
在这里插入图片描述
那么为什么采集的相机图片存在严重的掉帧现象?
原因是服务端默认为异步模式运行,即服务端尽可能地进行仿真,根本不管客户端是否跟上它的步伐。客户端采集并保存图片比较慢,因此存在掉帧现象。
那么如何让客户端和服务端同步尼?

异步模式和同步模式

参考 link,在 s i m u l a t i o n simulation simulation 里的时间与真实世界是不同的, s i m u l a t i o n simulation simulation 里没有"一秒"的概念,只有"一个 t i m e − s t e p time-step timestep" 的概念。这个 t i m e − s t e p time-step timestep c a r l a carla carla 服务端更新两个 s t e p step step 之间的时间间隔, 相当于仿真世界里进行了一次更新,这个更新在真实世界里的时间可能只有几毫秒。
仿真世界里的 t i m e − s t e p time-step timestep 其实有两种,一种是 V a r i a b l e t i m e − s t e p Variable time-step Variabletimestep, 另一种是 F i x e d t i m e − s t e p Fixed time-step Fixedtimestep .
V a r i a b l e t i m e − s t e p Variable time-step Variabletimestep c a r l a carla carla 服务端默认的运行模式。即每次仿真的步长所需要的真实时间是不一定的,可能这一步用了 3 m s 3ms 3ms,下一步用了 5 m s 5ms 5ms
代码如下:

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

F i x e d t i m e − s t e p Fixed time-step Fixedtimestep即固定时间步长,在这种时间步长设置下,每次 t i m e − s t e p time-step timestep 所消耗的时间是固定的,比如设置为 5 m s 5ms 5ms

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

这样设置完固定的 t i m e − s t e p time-step timestep 后,仿真的频率就一定会按照设定的来吗?
答案是不一定!存在这样的情况,在同步模式下,仿真服务端会等待客户端完成手头的工作后,再进行更新。假设设置的固定时间是 5 m s 5ms 5ms,而客户端存储操作需要 10 m s 10ms 10ms,这样的话服务端不会在 5 m s 5ms 5ms 之后就进行更新,而是等到存储操作完成后才进行下一次更新。
那么到底是怎么才能触发服务器端进行更新尼?
经过上述分析,肯定不是按照设定的时间进行更新,至于怎么触发更新,下方进行讲解。

设置同步模式

将 time-step 设置为 Fixed time-step

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

启动 traffic manager

在同步模式下,车辆要进行自动模式必须依附于开启同步模式的 t r a f f i c m a n a g e r traffic manager trafficmanager,不然虽然开启了自动模式,车辆会一直不动。

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)

设置 world.tick()函数和Queue的get函数

至于怎么触发更新,上述提到不是采用固定时间进行触发的,那么如何进行触发的尼?答案就是采用 w o r l d . t i c k ( ) world.tick() world.tick() 函数以及 Q u e u e Queue Queue g e t ( ) get() get() 进行触发的。
q u e u e . g e t queue.get queue.get 函数有一个功效,就是在它把队列里所有内容都提取出来之前,会阻止任何去阻止其他进程越过这一步,相当于一个 b l o c k e r blocker blocker。如果没有这个 q u e u e queue queue ,即使设置成了同步模式,还是会自己跑自己的,不会进行同步。
可以这样理解, s e t t i n g s . s y n c h r o n o u s _ m o d e settings.synchronous\_mode settings.synchronous_mode = T r u e True True ,让仿真的更新要通过这个 c l i e n t client client 来唤醒,但这并不能保证它会等该 c l i e n t client client 其他进程运行完,必须要再加一个 q u e u e queue queue 来阻挡一下它,逼迫它等着该客户其他线程搞定。

def sensor_callback(sensor_data, sensor_queue, sensor_name):
    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))
# create sensor queue
sensor_queue = Queue()
# 设置传感器,比如相机,然后
# set the callback function
camera.listen(lambda image: sensor_callback(image, sensor_queue, "camera"))
while True:
    world.tick()
    sensor_queue.get(True)

所以,总结下来,让服务端学会等待客户端的必要条件有三个,如下:

settings.synchronous_mode = True
world.tick()
Thread Blocker(such as Queue)

结束销毁

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

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

经过上述设置过程,可以实现同步模式。

示例

如下示例中,在车辆上安装了相机和雷达,然后同时采集数据,可以发现:采集的数据不存在掉帧现象,并且相机和雷达数据也会同步。

import random
import os
import sys
import glob
from queue import Queue
from queue import Empty
try:
    sys.path.append(glob.glob('../carla/dist/carla-*%d.%d-%s.egg' % (
        sys.version_info.major,
        sys.version_info.minor,
        'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
except IndexError:
    pass
import carla

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))

#创建同步模式,三步:
def main():
    actor_list = []
    sensor_list = []
    try:
        # First of all, we need to create the client that will send the requests, assume port is 2000
        client = carla.Client('localhost', 2000)
        client.set_timeout(2.0)
        # Retrieve the world that is currently running
        world = client.get_world()
        # world = client.load_world('Town02') # you can also retrive another world by specifically defining
        blueprint_library = world.get_blueprint_library()
        # Set weather for your world
        weather = carla.WeatherParameters(cloudiness=10.0,
                                          precipitation=10.0,
                                          fog_density=10.0)
        world.set_weather(weather)

        # set synchorinized mode,将time-step设置为Fixed time-step
        original_settings = world.get_settings()
        settings = world.get_settings()
        settings.fixed_delta_seconds = 0.05
        settings.synchronous_mode = True
        world.apply_settings(settings)
        #将同步模式设置为True
        #因为同步模式下车辆要使用自动模式必须开启同步模式的traffic manager。
        traffic_manager = client.get_trafficmanager()
        traffic_manager.set_synchronous_mode(True)

        # create sensor queue
        sensor_queue = Queue()

        # create the ego vehicle
        ego_vehicle_bp = blueprint_library.find('vehicle.mercedes-benz.coupe')
        # black color
        ego_vehicle_bp.set_attribute('color', '0, 0, 0')
        # get a random valid occupation in the world
        transform = random.choice(world.get_map().get_spawn_points())
        # spawn the vehilce
        ego_vehicle = world.spawn_actor(ego_vehicle_bp, transform)
        # set the vehicle autopilot mode
        ego_vehicle.set_autopilot(True)
        # collect all actors to destroy when we quit the script
        actor_list.append(ego_vehicle)

        # create directory for outputs
        output_path = './outputs/output_synchronized'
        if not os.path.exists(output_path):
            os.makedirs(output_path)

        # add a camera
        camera_bp = blueprint_library.find('sensor.camera.rgb')
        # camera relative position related to the vehicle
        camera_transform = carla.Transform(carla.Location(x=1.5, z=2.4))
        camera = world.spawn_actor(camera_bp, camera_transform, attach_to=ego_vehicle)
        # set the callback function
        camera.listen(lambda image: sensor_callback(image, sensor_queue, "camera"))
        sensor_list.append(camera)

        # we also add a lidar on it
        lidar_bp = blueprint_library.find('sensor.lidar.ray_cast')
        lidar_bp.set_attribute('channels', str(32))
        lidar_bp.set_attribute('points_per_second', str(90000))
        lidar_bp.set_attribute('rotation_frequency', str(40))
        lidar_bp.set_attribute('range', str(20))

        # set the relative location
        lidar_location = carla.Location(0, 0, 2)
        lidar_rotation = carla.Rotation(0, 0, 0)
        lidar_transform = carla.Transform(lidar_location, lidar_rotation)
        # spawn the lidar
        lidar = world.spawn_actor(lidar_bp, lidar_transform, attach_to=ego_vehicle)
        lidar.listen(
            lambda point_cloud: sensor_callback(point_cloud, sensor_queue, "lidar"))
        sensor_list.append(lidar)

        while True:
            #world.tick()函数,只出现于同步模式,意思是让simulation更新一次。
            world.tick()
            # set the sectator to follow the ego vehicle
            spectator = world.get_spectator()
            transform = ego_vehicle.get_transform()
            spectator.set_transform(carla.Transform(transform.location + carla.Location(z=20),
                                                    carla.Rotation(pitch=-90)))

            # As the queue is blocking, we will wait in the queue.get() methods
            # until all the information is processed and we continue with the next frame.
            try:
                #sensor_queue.get()函数的强制功能
                for i in range(0, len(sensor_list)):
                    s_frame = sensor_queue.get(True, 1.0)
                    print("    Frame: %d   Sensor: %s" % (s_frame[0], s_frame[1]))

            except Empty:
                print("   Some of the sensor information is missed")
    finally:
        world.apply_settings(original_settings)
        print('destroying actors')
        client.apply_batch([carla.command.DestroyActor(x) for x in actor_list])
        for sensor in sensor_list:
            sensor.destroy()
        print('done.')

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print(' - Exited by user.')
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值