(八) carla中同步模式设置
(八) carla中同步模式设置前言由于前一段时间在 carlacarlacarla 中采集数据时发现:采集的图片的帧数是跳帧的,不连续,通过 pygamepygamepygame 也可以看到 carlacarlacarla 服务端和客户端的频率也是不一样的。那么为什么采集的相机图片存在严重的掉帧现象?原因是服务端默认为异步模式运行,即服务端尽可能地进行仿真,根本不管客户端是否跟上它的步伐。客户端
(八) 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
time−step" 的概念。这个
t
i
m
e
−
s
t
e
p
time-step
time−step 是
c
a
r
l
a
carla
carla 服务端更新两个
s
t
e
p
step
step 之间的时间间隔, 相当于仿真世界里进行了一次更新,这个更新在真实世界里的时间可能只有几毫秒。
仿真世界里的
t
i
m
e
−
s
t
e
p
time-step
time−step 其实有两种,一种是
V
a
r
i
a
b
l
e
t
i
m
e
−
s
t
e
p
Variable time-step
Variabletime−step, 另一种是
F
i
x
e
d
t
i
m
e
−
s
t
e
p
Fixed time-step
Fixedtime−step .
V
a
r
i
a
b
l
e
t
i
m
e
−
s
t
e
p
Variable time-step
Variabletime−step 是
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 Fixedtime−step即固定时间步长,在这种时间步长设置下,每次 t i m e − s t e p time-step time−step 所消耗的时间是固定的,比如设置为 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
time−step 后,仿真的频率就一定会按照设定的来吗?
答案是不一定!存在这样的情况,在同步模式下,仿真服务端会等待客户端完成手头的工作后,再进行更新。假设设置的固定时间是
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.')
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)