“ 经常有朋友问如何学习仿真测试,于是想着把自己的一些经验和理解分享出来,希望能有所帮助。不过视野和技术有限,所说不一定对,供大家批评和参考。这是第20篇,CARLA的传感器。”
自动驾驶算法通过各种传感器获取周围环境状态,从而进行正确决策控制;传感器的仿真也是自动驾驶仿真的重要内容。本节介绍CARLA中传感器模型的种类,并以摄像头和物体传感器为例说明传感器模型的用法。
CARLA中的传感器种类
根据信息更新条件的不同可以将CARLA中的传感器分为周期型和事件型两个大的类别,简述如下:
(1)周期型传感器
根据设定的周期更新数据,包括的类型如下表:
(2)事件型传感器
相关事件发生时更新数据,包括的类型如下表:
Pygame的简介
Pygame 是基于 SDL库开发的游戏软件库,主要用于2D游戏开发,不仅可以进行渲染显示,还可以采集键盘事件并进行响应,在CARLA的很多官方示例中使用其进行显示和交互操作,如前面已经使用过的manual_control.py。后面传感器的实例中也使用Pygame进行摄像头信息的显示,所以我们在这里先对Pygame的使用方法进行一下简单说明。
Pygame的最小开发框架如下:
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill("purple")
pygame.display.flip()
pygame.quit()
传感器的添加和使用
传感器的添加包含四个步骤:获取蓝图,配置传感器参数,将传感器绑定至车辆,定义并配置监听方法。下面以常规摄像头RGB camera和障碍物传感器Obstacle detector为例说明传感器模型的用法。
代码第57~83行添加了ego车和50辆障碍车并为其配置了自动驾驶,这部分内容在上一节出现过,不在赘述。由于本实例中要添加传感器,为实现传感器的消息稳定和同步,将仿真设置为了同步定步长模式,于是也将Traffic Manager设置为了同步模式(代码第46~55行)。
由于对仿真世界的触发和其他处理会在每次循环中会占用一定的时间,而且该时间可能每次循环会有细微的差别,为保证运行周期的稳定,采用预设步长减去实际运行时间的结果作为每次循环的实际等待时间,如代码第143~146行。
代码第85~116行为ego车配置了常规摄像头RGB camera和障碍物传感器Obstacle detector。传感器的配置分为四步:
(1)获取蓝图
每种传感器都有专门的id,可以通过BlueprintLibrary的find方法获取相应id的传感器,如代码第88和104行所示分别获取了常规摄像头RGB camera和障碍物传感器Obstacle detector。
(2)配置传感器参数
不同传感器类型的参数各有不同,为实现满意的仿真效果,需要根据模拟的真实传感器的特性和测试需求,设置相应参数,并根据仿真效果,对参数进行调整。
本实例中所使用参数的含义如下表所示:
下面对障碍物传感器的探测原理进行简要说明,方便对其参数的理解。可以将障碍物探测传感器理解为一台安装在车辆上的大炮,其安装位置和方向由其绑定至车辆的位姿transform确定,该大炮每隔sensor_tick时间向前发射一枚半径为hit_radius的球形炮弹,炮弹的最远飞行distance距离,被炮弹击中的物体即为探测到的物体。所以建议将hit_radius设置为较小值(如0.5),以避免探测到多余的物体。
(3)绑定至车辆
通过world.try_spawn_actor的attach_to参数设置传感器安装的车辆,通过transform参数设置相对于车辆几何中心的安装位置,如代码第94~96和110~112所示。
(4)定义并配置监听方法
通过传感器的listen方法配置监听方法,该方法为回调callback的形式,当传感器收到信息后,调用该方法并将传感器信息传入。由该方法对传感器信息进行处理,如代码第100和116行所示。
代码第7~20和22~31行分别是常规摄像头RGB camera和障碍物传感器Obstacle detector的监听方法。对于摄像头,将其图像显示在了pygame的窗口上,方便运行时查看;对于障碍物传感器,对其探测到的障碍物信息进行了打印。
完整代码如下:
import carla
import numpy as np
import pygame
import random
import time
def rgb_camera_callback(image, pygame_display):
'''RGB camera的监听方法
'''
# print(f"-- rgb camera callback time = {time.time()}")
#在pygame窗口显示
image.convert(carla.ColorConverter.Raw)
array = np.frombuffer(image.raw_data, dtype=np.dtype("uint8"))
array = np.reshape(array, (image.height, image.width, 4))
array = array[:, :, :3]
array = array[:, :, ::-1]
surface = pygame.surfarray.make_surface(array.swapaxes(0, 1))
pygame_display.blit(surface, (0, 0))
#保存至文件
# image.save_to_disk(f"image/{image.frame}_{image.timestamp}.png")
def obstacle_detector_callback(obs):
'''obstacle detector的监听方法
'''
# print(f"-- obstacle detector callback time = {time.time()}")
ego_tf = obs.actor.get_transform()
other_actor_id = obs.other_actor.id
other_actor_tf = obs.other_actor.get_transform()
print(f"ego transform = {ego_tf}")
print(f"other actor id = {other_actor_id}, transform = {other_actor_tf}")
print(f"distance = {obs.distance}")
def main():
#创建client,并获取world
client = carla.Client("localhost", 2000)
world = client.get_world()
original_settings = world.get_settings()
vehicles = []
sensors = []
try:
#初始化pygame
pygame.init()
pygame.font.init()
pygame_display = pygame.display.set_mode([800, 600], pygame.HWSURFACE | pygame.DOUBLEBUF)
#设置模式为:同步定步长,且步长为0.05s
time_step = 0.05
settings = world.get_settings()
settings.synchronous_mode = True
settings.fixed_delta_seconds = time_step
world.apply_settings(settings)
#设置traffic manager为同步模式
traffic_manager = client.get_trafficmanager()
traffic_manager.set_synchronous_mode(True)
#选择一些障碍车的车型
models = ['dodge', 'audi', 'model3', 'mini', 'mustang', 'lincoln', 'prius', 'nissan', 'crown', 'impala']
blueprints = []
for vehicle in world.get_blueprint_library().filter('*vehicle*'):
if any(model in vehicle.id for model in models):
blueprints.append(vehicle)
#设置生成障碍车的数量
spawn_points = world.get_map().get_spawn_points()
max_vehicles = 50
max_vehicles = min([max_vehicles, len(spawn_points)])
#生成障碍车并设置自动驾驶
for i, spawn_point in enumerate(random.sample(spawn_points, max_vehicles)):
vehicle = world.try_spawn_actor(random.choice(blueprints), spawn_point)
if vehicle is not None:
vehicles.append(vehicle)
vehicle.set_autopilot(True)
#生成ego车
ego_bp = world.get_blueprint_library().find('vehicle.tesla.model3')
ego_bp.set_attribute('role_name','ego')
ego_spawn_point = random.choice(spawn_points)
ego_vehicle = world.try_spawn_actor(ego_bp, ego_spawn_point)
if ego_vehicle is not None:
vehicles.append(ego_vehicle)
ego_vehicle.set_autopilot(True)
sensors = []
#为ego车配置RGB camera
#1.获取blueprint
rgb_camera_bp = world.get_blueprint_library().find('sensor.camera.rgb')
#2.配置参数
rgb_camera_bp.set_attribute('image_size_x', '800')
rgb_camera_bp.set_attribute('image_size_y', '600')
rgb_camera_bp.set_attribute('fov', '110')
rgb_camera_bp.set_attribute('sensor_tick', '0.1')
#3.绑定至车辆
rgb_camera_tf = carla.Transform(carla.Location(x=1.0, z=2.0), carla.Rotation(yaw=0.0))
rgb_camera = world.try_spawn_actor(rgb_camera_bp, rgb_camera_tf, attach_to=ego_vehicle)
if rgb_camera is not None:
sensors.append(rgb_camera)
#4.配置监听方法
rgb_camera.listen(lambda image: rgb_camera_callback(image, pygame_display))
#为ego车配置Obstacle detector
#1.获取blueprint
obstacle_detector_bp = world.get_blueprint_library().find('sensor.other.obstacle')
#2.配置参数
obstacle_detector_bp.set_attribute('distance', '100')
obstacle_detector_bp.set_attribute('hit_radius', '0.5')
obstacle_detector_bp.set_attribute('only_dynamics', 'True')
obstacle_detector_bp.set_attribute('sensor_tick', '0.0')
#3.绑定至车辆
obstacle_detector_tf = carla.Transform(carla.Location(x=1.0, z=2.0), carla.Rotation(yaw=0.0))
obstacle_detector = world.try_spawn_actor(obstacle_detector_bp, obstacle_detector_tf, attach_to=ego_vehicle)
if obstacle_detector is not None:
sensors.append(obstacle_detector)
#4.配置监听方法
obstacle_detector.listen(lambda obs: obstacle_detector_callback(obs))
#运行
running = True
while running:
start_time = time.time()
##start of own scripts
ret = world.tick()
snap = world.get_snapshot()
frame = snap.timestamp.frame
print(f"---------- frame = {frame} ----------")
#设置spectator跟随ego车
spectator = world.get_spectator()
ego_tf = ego_vehicle.get_transform()
spectator_tf = carla.Transform(ego_tf.location, ego_tf.rotation)
spectator_tf.location += ego_tf.get_forward_vector() * (-10)
spectator_tf.location += ego_tf.get_up_vector() * 3
spectator.set_transform(spectator_tf)
#pygame
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.display.flip()
##end of own scripts
end_time = time.time()
sleep_time = time_step - (end_time - start_time)
if sleep_time > 0:
time.sleep(sleep_time)
finally:
pygame.quit()
world.apply_settings(original_settings)
for actor in vehicles + sensors:
actor.destroy()
if __name__ == "__main__":
main()