4、Carla传感器详解:RGB相机、深度相机、语义分割相机、Lidar、Radar、IMU、GNSS
传感器是自动驾驶的「眼睛」和「耳朵」。在Carla里,你几乎能找到所有主流传感器的仿真实现。我个人习惯把传感器分为两类:视觉类(相机系列)和物理感知类(雷达、IMU等)。今天咱们一个一个过,每个我都会讲讲实际项目里踩过的坑。
4.1 RGB相机——最基础的视觉输入
RGB相机就是咱们平时说的普通彩色摄像头。在Carla里,它输出的是三通道的彩色图像,每个像素值范围0-255。
核心参数:图像分辨率、视场角(FOV)、快门速度、感光度。
我刚开始做仿真时,直接用了默认参数,结果发现模型在仿真里跑得好好的,一上实车就崩。后来才意识到——仿真相机的FOV必须和实车一致。你想想看,如果仿真里用的是90度FOV,实车是120度,那模型看到的视野完全不同,训练出来的感知模型肯定水土不服。
# 创建RGB相机的标准写法
blueprint = world.get_blueprint_library().find('sensor.camera.rgb')
blueprint.set_attribute('image_size_x', '1920')
blueprint.set_attribute('image_size_y', '1080')
blueprint.set_attribute('fov', '90')
camera = world.spawn_actor(blueprint, transform, attach_to=vehicle)
避坑指南:我曾经在项目里把FOV设成了120度,结果图像边缘畸变严重,模型一直误检。后来老老实实改成90度,问题就解决了。建议你一开始就用90度FOV,这是最通用的配置。
4.2 深度相机——给图像加上距离信息
深度相机输出的不是彩色图,而是每个像素到相机的距离。说白了,它告诉你「这个物体离我有多远」。
Carla的深度相机有两种输出模式:
- 线性深度:直接输出距离值,单位米
- 归一化深度:把距离映射到0-1之间
我个人更推荐用线性深度,因为后期处理方便。归一化深度虽然看着直观,但做点云投影时还得反算回去,多一步操作就多一个出错的可能。
# 深度相机配置
depth_bp = world.get_blueprint_library().find('sensor.camera.depth')
depth_bp.set_attribute('image_size_x', '800')
depth_bp.set_attribute('image_size_y', '600')
depth_camera = world.spawn_actor(depth_bp, transform, attach_to=vehicle)
注意:深度相机的有效范围是有限制的。我记得有一次做远距离障碍物检测,发现超过100米的物体深度值全是0。查了半天文档才发现,默认最大深度就是100米。如果你需要更远的探测距离,记得手动设置。
4.3 语义分割相机——像素级别的「贴标签」
这个传感器厉害了。它输出的图像里,每个像素都对应一个语义类别。比如路面是紫色、车辆是蓝色、行人是红色。你想想看,如果要做语义分割模型的训练,这简直就是现成的标注数据。
Carla内置了23种语义类别,包括:
| 类别ID | 名称 | 颜色 |
|---|---|---|
| 0 | 未标注 | 黑色 |
| 1 | 路面 | 紫色 |
| 2 | 车道线 | 白色 |
| 4 | 车辆 | 蓝色 |
| 5 | 行人 | 红色 |
嗯,这里要注意:语义分割相机输出的不是普通图片,而是标签图。每个像素的值就是类别ID,而不是RGB颜色。如果你直接保存成jpg,那颜色就全乱了。正确的做法是保存成PNG格式,或者用numpy数组直接处理。
# 语义分割相机回调处理
def process_semantic(image):
# 将原始数据转为numpy数组
array = np.frombuffer(image.raw_data, dtype=np.uint8)
array = array.reshape((image.height, image.width, 4))
# 取第一个通道,就是类别ID
labels = array[:, :, 0]
# 保存为npy格式,方便后续使用
np.save('semantic_labels.npy', labels)
我的经验:做数据标注时,我经常把RGB相机和语义分割相机同时开启。这样既能拿到原始图像,又能拿到精准的标注结果。省去了人工标注的麻烦,效率提升不是一星半点。
4.4 Lidar——自动驾驶的「主力传感器」
Lidar(激光雷达)是自动驾驶里最重要的传感器之一。它通过发射激光束,测量周围物体的距离和形状。Carla支持多种Lidar模型,包括旋转式和固态式。
核心参数有这些:
- 通道数:16线、32线、64线、128线
- 水平视场角:通常360度
- 垂直视场角:比如-15度到+15度
- 探测距离:最大100米左右
- 点云密度:每秒点数
我建议你从32线Lidar开始。为什么呢?16线太稀疏,做目标检测效果差;64线虽然好,但计算量大,仿真跑起来卡。32线是个不错的平衡点。
# 创建32线Lidar
lidar_bp = world.get_blueprint_library().find('sensor.lidar.ray_cast')
lidar_bp.set_attribute('channels', '32')
lidar_bp.set_attribute('range', '100')
lidar_bp.set_attribute('points_per_second', '100000')
lidar_bp.set_attribute('rotation_frequency', '10') # 10Hz
lidar = world.spawn_actor(lidar_bp, transform, attach_to=vehicle)
曾经踩过的坑:有一次我设置points_per_second为500000,结果仿真帧率直接掉到个位数。后来发现,点云数量太大,CPU处理不过来。建议你根据硬件性能调整这个参数,一般100000到200000就够用了。
4.5 Radar——雨雾天气的「救星」
Radar(毫米波雷达)和Lidar不同,它用无线电波而不是激光。好处是不受天气影响,雨雾天照样工作。坏处是分辨率低,只能检测到物体的大致位置和速度。
Carla的Radar输出的是检测点列表,每个点包含:
- 距离
- 方位角
- 速度(径向速度)
- 信号强度
我个人觉得,Radar在仿真里最实用的场景是融合感知。比如Lidar检测到障碍物,Radar提供速度信息,两者结合就能判断物体是静止还是运动。
# Radar配置示例
radar_bp = world.get_blueprint_library().find('sensor.other.radar')
radar_bp.set_attribute('horizontal_fov', '30') # 水平视场角
radar_bp.set_attribute('vertical_fov', '10') # 垂直视场角
radar_bp.set_attribute('range', '100') # 探测距离
radar = world.spawn_actor(radar_bp, transform, attach_to=vehicle)
小技巧:Radar的检测点数量有限,默认可能只有几十个。如果你需要更密集的检测结果,可以调整相关参数。不过要注意,点太多也会影响性能。
4.6 IMU——车辆的「体感传感器」
IMU(惯性测量单元)测量的是车辆的加速度和角速度。说白了,它告诉你车在怎么加速、怎么转弯。
Carla的IMU输出:
- 三轴加速度(m/s²)
- 三轴角速度(rad/s)
- 朝向(四元数)
嗯,这里要注意:IMU的数据是有噪声的。Carla默认会添加高斯噪声,模拟真实传感器的误差。如果你做的是高精度定位算法,这个噪声参数需要仔细调。
# IMU配置
imu_bp = world.get_blueprint_library().find('sensor.other.imu')
imu_bp.set_attribute('noise_accel_stddev_x', '0.01')
imu_bp.set_attribute('noise_accel_stddev_y', '0.01')
imu_bp.set_attribute('noise_gyro_stddev', '0.001')
imu = world.spawn_actor(imu_bp, transform, attach_to=vehicle)
我的建议:做定位算法测试时,先把噪声设小一点,等算法跑通了再逐步加大噪声。这样能快速定位问题出在算法本身还是噪声处理上。
4.7 GNSS——全球定位系统
GNSS(全球导航卫星系统)就是咱们常说的GPS。它输出车辆的经纬度坐标和海拔高度。
Carla的GNSS默认会添加一些误差,模拟真实环境下的定位偏差。你可以设置:
- 经纬度噪声标准差
- 海拔噪声标准差
- 更新频率
我记得有一次做多传感器融合定位,发现GNSS和IMU的数据对不上。查了半天,原来是GNSS的更新频率设成了1Hz,而IMU是100Hz。两者时间戳没对齐,导致融合结果乱七八糟。后来我把GNSS频率调到10Hz,问题就解决了。
# GNSS配置
gnss_bp = world.get_blueprint_library().find('sensor.other.gnss')
gnss_bp.set_attribute('noise_alt_stddev', '0.001')
gnss_bp.set_attribute('noise_lat_stddev', '0.001')
gnss_bp.set_attribute('noise_lon_stddev', '0.001')
gnss_bp.set_attribute('sensor_tick', '0.1') # 10Hz
gnss = world.spawn_actor(gnss_bp, transform, attach_to=vehicle)
重要提醒:GNSS在室内或隧道里是收不到信号的。如果你做的是地下停车场场景,记得关闭GNSS或者模拟信号丢失的情况。不然你的定位算法会一直以为自己还在原地。
4.8 多传感器同步——实战中的关键
单个传感器讲完了,但实际项目中,多个传感器的时间同步才是最大的坑。
我建议你这样做:
- 所有传感器使用同一个世界时钟(Carla默认就是)
- 设置相同的sensor_tick参数,比如0.1秒(10Hz)
- 在回调函数里记录时间戳,方便后期对齐
# 多传感器同步示例
sensors = []
for bp in [rgb_bp, depth_bp, lidar_bp, radar_bp]:
bp.set_attribute('sensor_tick', '0.1') # 统一10Hz
sensor = world.spawn_actor(bp, transform, attach_to=vehicle)
sensor.listen(lambda data: process_data(data))
sensors.append(sensor)
最后说一句:传感器配置没有绝对的标准答案。不同的场景、不同的算法,对传感器的需求都不一样。我的做法是:先按默认参数跑起来,然后根据实际效果逐步调整。别一开始就追求完美配置,那反而容易把自己绕进去。
好了,传感器这块就讲到这里。下一章咱们聊聊怎么用这些传感器采集数据,以及如何做自动标注。到时候我会分享一些我实际项目里用过的工具和脚本,保证实用。