4. 传感器配置:相机传感器(RGB、深度、语义分割)、Lidar传感器、IMU/GNSS传感器

好了,咱们进入正题。传感器配置这块,说白了就是给自动驾驶车装上「眼睛」和「耳朵」。我在项目里见过不少同学,上来就堆传感器,结果数据对不上,跑起来全是坑。今天咱们一个一个捋清楚。

4.1 相机传感器:RGB、深度、语义分割

相机是感知系统的基础。Carla里相机分三种:RGB、深度、语义分割。它们共用一套配置接口,但输出数据完全不同。

4.1.1 RGB相机

RGB相机最直观,输出就是彩色图像。我个人习惯用1920x1080分辨率,帧率设20。太高了CPU扛不住,太低了感知算法会卡顿。

# 配置RGB相机
rgb_camera_bp = blueprint_library.find('sensor.camera.rgb')
rgb_camera_bp.set_attribute('image_size_x', '1920')
rgb_camera_bp.set_attribute('image_size_y', '1080')
rgb_camera_bp.set_attribute('fov', '90')
rgb_camera_bp.set_attribute('sensor_tick', '0.05')  # 20fps

# 挂载到车辆上
rgb_camera = world.spawn_actor(
    rgb_camera_bp,
    transform.Transform(
        location=Location(x=1.5, z=2.4),
        rotation=Rotation(pitch=-15)
    ),
    attach_to=vehicle
)
小技巧:相机挂载位置很关键。我一般放在车顶前部,高度2.4米,俯仰角-15度。这样既能看清近处路面,又能兼顾远处交通标志。

4.1.2 深度相机

深度相机输出的是每个像素到物体的距离。嗯,这里要注意:Carla的深度数据是归一化到[0,1]的,需要自己换算成实际米数。

# 深度相机配置
depth_camera_bp = blueprint_library.find('sensor.camera.depth')
depth_camera_bp.set_attribute('image_size_x', '640')
depth_camera_bp.set_attribute('image_size_y', '480')
depth_camera_bp.set_attribute('fov', '90')

# 深度数据回调处理
def process_depth(image):
    # 将归一化深度转为实际距离(米)
    depth_data = np.array(image.raw_data)[:, :, 0]
    depth_in_meters = depth_data * 1000.0  # Carla默认最大1000米
    # 或者用官方方法
    depth_in_meters = image.convert_to_color_vector()
避坑指南:我曾经在深度数据上栽过跟头。Carla的深度图是「视差图」格式,不是直接的距离图。如果你直接用raw_data做计算,记得先归一化。否则训练出来的模型,放到真实场景里全废了。

4.1.3 语义分割相机

语义分割相机输出的是每个像素的标签ID。Carla内置了23个类别,比如道路、车辆、行人、建筑等。这个在训练感知模型时特别好用,相当于有了「标准答案」。

# 语义分割相机
seg_camera_bp = blueprint_library.find('sensor.camera.semantic_segmentation')
seg_camera_bp.set_attribute('image_size_x', '1280')
seg_camera_bp.set_attribute('image_size_y', '720')

# 标签映射表(部分)
LABEL_MAP = {
    0: 'Unlabeled',
    1: 'Road',
    2: 'Sidewalk',
    3: 'Building',
    4: 'Wall',
    5: 'Fence',
    6: 'Pole',
    7: 'TrafficLight',
    8: 'TrafficSign',
    9: 'Vegetation',
    10: 'Terrain',
    11: 'Sky',
    12: 'Pedestrian',
    13: 'Rider',
    14: 'Car',
    15: 'Truck',
    16: 'Bus',
    17: 'Train',
    18: 'Motorcycle',
    19: 'Bicycle',
    20: 'Static',
    21: 'Dynamic',
    22: 'Other'
}

核心要点:三种相机可以同时挂载,互不干扰。但要注意CPU/GPU负载。我建议:训练时开全三种,测试时只开RGB和深度,节省资源。

4.2 Lidar传感器

Lidar是自动驾驶的「硬核」传感器。Carla支持多种Lidar模型,最常用的是32线和64线旋转式激光雷达。

4.2.1 基本配置

# 配置64线Lidar
lidar_bp = blueprint_library.find('sensor.lidar.ray_cast')
lidar_bp.set_attribute('channels', '64')          # 线数
lidar_bp.set_attribute('range', '100')            # 探测距离(米)
lidar_bp.set_attribute('points_per_second', '1000000')  # 每秒点数
lidar_bp.set_attribute('rotation_frequency', '20')      # 旋转频率(Hz)
lidar_bp.set_attribute('upper_fov', '15')         # 上视角
lidar_bp.set_attribute('lower_fov', '-25')        # 下视角

# 挂载到车顶中心
lidar = world.spawn_actor(
    lidar_bp,
    transform.Transform(
        location=Location(x=0.0, z=2.5)
    ),
    attach_to=vehicle
)

为什么选64线?说实话,32线也能用,但点云稀疏,远距离物体容易漏检。我在做高速场景测试时,64线明显更稳。当然,如果你只是做园区低速场景,32线完全够用。

4.2.2 点云数据处理

def process_lidar_data(point_cloud):
    # 获取点云坐标(x, y, z)
    points = np.frombuffer(point_cloud.raw_data, dtype=np.dtype('f4'))
    points = np.reshape(points, (int(points.shape[0] / 4), 4))
    
    # 过滤掉地面点(z < -1.5)
    ground_mask = points[:, 2] > -1.5
    filtered_points = points[ground_mask]
    
    # 转换为Open3D格式可视化
    import open3d as o3d
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(filtered_points[:, :3])
    return pcd
经验之谈:Lidar数据量很大,每秒上百万个点。我建议在回调函数里做降采样,比如每5帧处理一次。否则你的Python脚本会卡成PPT。

4.3 IMU/GNSS传感器

IMU和GNSS是定位系统的核心。IMU提供加速度和角速度,GNSS提供经纬度坐标。两者配合使用,才能实现高精度定位。

4.3.1 IMU配置

# IMU传感器
imu_bp = blueprint_library.find('sensor.other.imu')
imu_bp.set_attribute('sensor_tick', '0.01')  # 100Hz采样

imu = world.spawn_actor(
    imu_bp,
    transform.Transform(
        location=Location(x=0.0, z=0.0)  # 放在车辆质心
    ),
    attach_to=vehicle
)

# IMU数据回调
def process_imu(data):
    # 加速度(m/s²)
    accel = data.accelerometer
    # 角速度(rad/s)
    gyro = data.gyroscope
    # 朝向(四元数)
    compass = data.compass
    print(f"加速度: ({accel.x:.2f}, {accel.y:.2f}, {accel.z:.2f})")
注意:Carla的IMU数据是理想化的,没有噪声。真实IMU有漂移和噪声。如果你要做定位算法验证,记得自己加高斯噪声。我一般加0.1m/s²的加速度噪声和0.01rad/s的角速度噪声。

4.3.2 GNSS配置

# GNSS传感器
gnss_bp = blueprint_library.find('sensor.other.gnss')
gnss_bp.set_attribute('sensor_tick', '1.0')  # 1Hz更新
gnss_bp.set_attribute('noise_alt', '0.0')    # 高度噪声
gnss_bp.set_attribute('noise_lat', '0.0')    # 纬度噪声
gnss_bp.set_attribute('noise_lon', '0.0')    # 经度噪声

gnss = world.spawn_actor(
    gnss_bp,
    transform.Transform(
        location=Location(x=0.0, z=0.0)
    ),
    attach_to=vehicle
)

# GNSS数据回调
def process_gnss(data):
    lat = data.latitude
    lon = data.longitude
    alt = data.altitude
    print(f"位置: ({lat:.6f}, {lon:.6f}, {alt:.2f}m)")

GNSS默认不带噪声,但你可以手动开启。我建议做仿真时加一点噪声,这样更接近真实情况。不过要注意,Carla的GNSS坐标是基于UE4世界的,不是真实经纬度。

4.4 传感器同步与时间戳

多传感器协同工作时,时间同步是个大问题。Carla提供了两种模式:固定时间步和异步模式。

# 设置固定时间步(推荐)
settings = world.get_settings()
settings.fixed_delta_seconds = 0.05  # 20fps
settings.synchronous_mode = True
world.apply_settings(settings)

# 同步模式下,所有传感器在同一时刻触发
world.tick()  # 推进一帧

最佳实践:我强烈建议使用同步模式。异步模式下,相机和Lidar的时间戳可能差几十毫秒,这对融合算法是致命的。同步模式虽然慢一点,但数据对齐可靠。

4.5 传感器配置总结

传感器类型 推荐配置 输出频率 数据量 典型用途
RGB相机 1920x1080, FOV 90° 20fps 中等 目标检测、车道线识别
深度相机 640x480, FOV 90° 20fps 距离估计、避障
语义分割 1280x720 20fps 中等 训练数据生成
Lidar 64线, 100m范围 20Hz 3D感知、建图
IMU 100Hz采样 100Hz 极低 位姿估计
GNSS 1Hz更新 1Hz 极低 全局定位

最后说一句:传感器配置没有「最好」,只有「最合适」。你想想看,做园区低速配送,32线Lidar加两个RGB相机就够了。但要做高速自动驾驶,64线Lidar加环视相机是标配。我在项目里一般先按「够用」配置,跑通后再根据需求增减。这样既不会浪费资源,也不会漏掉关键数据。

我的习惯:每次配置新传感器,我都会先跑一个「数据采集测试」,看看输出格式、频率、数据范围是否正常。这一步花不了10分钟,但能避免后面调试时抓狂。