3、传感器基础:传感器类型概览、传感器Attachment规则、传感器Transform设置、传感器回调函数

好,咱们进入第三节。传感器这块,是自动驾驶仿真的「眼睛」和「耳朵」。没有传感器,车就是个瞎子。我在做项目时,经常看到新手一上来就堆传感器,结果数据乱成一锅粥。今天咱们就把传感器的基础打扎实。

3.1 传感器类型概览

Carla 里内置了十几种传感器。我按用途分了三类,你记一下:

  • 视觉类:RGB相机、深度相机、语义分割相机、DVS事件相机
  • 测距类:LIDAR(激光雷达)、RADAR(毫米波雷达)
  • 状态类:GNSS(全球定位)、IMU(惯性测量单元)、速度计、碰撞检测器

我个人习惯,做城市道路场景时,至少配一套「前视RGB + 64线LIDAR + GNSS/IMU」。为什么?RGB看语义,LIDAR看距离,GNSS/IMU给位姿。三样凑齐,大部分算法都能跑。

小提示:DVS事件相机适合高速运动场景,但数据格式特殊,处理起来比较麻烦。新手可以先跳过。

3.2 传感器Attachment规则

传感器怎么「贴」到车上?Carla 提供了两种附着方式:

附着类型 说明 适用场景
AttachmentType.Rigid 刚性附着,传感器与车体完全固定 LIDAR、RADAR、相机(需要绝对稳定)
AttachmentType.SpringArm 弹簧臂附着,带缓冲效果 第三人称视角、跟拍相机

嗯,这里要注意:Rigid 是默认选项。SpringArm 我一般只用在调试视角上,不会用在正式数据采集里。为什么?因为弹簧臂会引入额外的运动延迟,你想想看,LIDAR 点云如果带了这个延迟,标定就全乱了。

避坑指南:我曾经在采集数据时,把LIDAR设成了SpringArm附着。结果点云和图像对不上,折腾了两天才发现是附着类型的问题。所以,量产数据采集,一律用Rigid。

3.3 传感器Transform设置

Transform 就是传感器的位置和朝向。说白了,就是告诉 Carla:「这个相机装在车顶哪个位置?朝哪个方向看?」

设置方式很简单:

# 创建一个相机,装在车顶前方
camera_bp = blueprint_library.find('sensor.camera.rgb')
camera_transform = carla.Transform(
    carla.Location(x=1.5, z=2.4),   # 车前方1.5米,高度2.4米
    carla.Rotation(pitch=-15.0)      # 向下倾斜15度
)
camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle)

这里有几个关键点:

  • 坐标系:x向前,y向左,z向上。这是UE4的左手系,别搞反了。
  • Rotation:pitch是俯仰(正数向上),yaw是偏航(正数向左转),roll是翻滚。
  • 相对位置:Transform是相对于父级(车辆)的。车动了,传感器跟着动。

我建议你画个草图。把车看成一个长方体,传感器装在哪个角,坐标就是多少。我在项目中遇到过,有人把LIDAR的z设成了负数,结果传感器埋到地底下去了,啥也扫不到。

经验之谈:LIDAR一般装在车顶中心,z=2.0~2.5米。前视相机装在挡风玻璃后面,x=1.2~1.8米,z=1.2~1.5米。这个范围基本覆盖了大多数乘用车。

3.4 传感器回调函数

回调函数,是传感器数据流的「水龙头」。传感器每生成一帧数据,就会调用你注册的函数。你在这个函数里处理数据、保存数据、或者做可视化。

基本写法:

def process_image(image):
    # image 是 sensor data 对象
    array = np.frombuffer(image.raw_data, dtype=np.dtype("uint8"))
    array = np.reshape(array, (image.height, image.width, 4))
    # 去掉alpha通道
    array = array[:, :, :3]
    # 保存或处理
    cv2.imwrite(f"frame_{image.frame}.png", array)

# 注册回调
camera.listen(process_image)

注意几个细节:

  • 数据类型:不同传感器返回的数据类型不同。RGB返回carla.Image,LIDAR返回carla.LidarMeasurement,GNSS返回carla.GNSSMeasurement
  • 帧率:回调函数会被频繁调用。如果你的处理逻辑太慢,会造成数据堆积。我一般会在回调里只做「轻量处理」,比如格式转换,然后丢到队列里,另起线程做保存。
  • 线程安全:回调是在独立线程里跑的。如果你要更新UI或者写文件,记得加锁。
小技巧:可以用image.save_to_disk()直接保存,但我不推荐。因为它的保存路径和命名规则不够灵活。自己写个保存函数,可控性更强。

好了,传感器基础就这些。说白了就是三件事:选对类型、贴对位置、接对回调。下一节咱们会深入讲RGB相机的具体配置,包括分辨率、FOV、曝光等参数。到时候你会看到,同样的场景,参数不同,拍出来的图天差地别。