4、RGB相机配置:相机参数详解

好,咱们今天来聊聊RGB相机。说实话,在自动驾驶仿真里,RGB相机是最常用的传感器之一。我刚开始做Carla项目时,第一个配置的就是它。你想想看,没有图像数据,很多视觉算法根本跑不起来。

这一节,我会把相机参数、图像数据结构、保存方法,还有畸变模型,一次性讲清楚。嗯,咱们直接开始。

4.1 相机参数详解

配置RGB相机,说白了就是调几个关键参数。我习惯把它们分成三类:光学参数、分辨率参数、位置参数。

4.1.1 FOV(视场角)

FOV,全称Field of View,就是相机能看多宽。单位是度。Carla里默认是90度,但我建议你根据场景来调。

  • 小FOV(60°以下):适合远距离目标检测,比如高速场景。视野窄,但远处物体更清晰。
  • 大FOV(120°以上):适合城市道路、交叉口。视野宽,但边缘畸变会明显。
我的经验:做城市自动驾驶时,我一般用90-110度。太宽了边缘畸变严重,太窄了又看不到侧向来车。你可以先设90度,跑一圈看看效果再调。

4.1.2 分辨率

分辨率决定了图像的清晰度。Carla里常用的分辨率有:

分辨率 宽×高 适用场景
640×480 快速测试、模型训练前验证
1280×720 大多数场景,平衡性能与质量
1920×1080 高精度感知、最终评测

我个人习惯先用640×480做调试,跑通了再换高分辨率。为什么呢?因为高分辨率图像数据量大,处理起来慢。你想想看,一帧1920×1080的图像,比640×480大了好几倍,调试时反复加载,浪费时间。

4.1.3 位置参数

相机的位置和朝向,直接决定了它能看到什么。Carla里用Transform来设置,包含Location(位置)和Rotation(旋转)。

# 设置相机位置:车顶前方
camera_transform = carla.Transform(
    carla.Location(x=1.5, z=2.4),  # x向前,z向上
    carla.Rotation(pitch=-15)       # 向下倾斜15度
)

这里要注意几点:

  • x:前后方向,正数向前
  • z:高度,一般1.5-2.5米,模拟车顶安装
  • pitch:俯仰角,负值向下看。我一般设-15到-20度,能看到近处路面
我曾经踩过的坑:有一次我把pitch设成了正15度,结果相机朝天拍了半天,啥也看不到。嗯,方向搞反了。所以记得:pitch负值向下,正值向上。

4.2 图像数据结构

相机采集到的图像,在Carla里是一个carla.Image对象。它包含什么?说白了就是一张图片的原始数据。

核心属性:

  • width, height:图像宽高
  • raw_data:原始像素数据,BGRA格式,每个像素4字节
  • frame:帧序号,可以用来同步多个传感器
  • timestamp:时间戳,精确到微秒

你可能会问:为什么是BGRA而不是RGB?嗯,这是Carla内部存储格式,我们后面转成RGB就行。

# 从raw_data中提取RGB数据
import numpy as np

def image_to_rgb_array(image):
    # 将raw_data转为numpy数组
    img = np.frombuffer(image.raw_data, dtype=np.uint8)
    img = img.reshape((image.height, image.width, 4))
    # 去掉Alpha通道,只保留RGB
    img = img[:, :, :3]
    # Carla是BGRA,需要转为RGB
    img = img[:, :, ::-1]
    return img

这段代码我用了很多次。核心就是:先reshape成二维数组,再取前三个通道,最后反转顺序。搞定。

4.3 保存图像到本地

保存图像,Carla自带了save_to_disk方法。但我建议你加上自己的命名规则,方便管理。

# 保存图像到本地
def save_image(image, save_path):
    # 自定义文件名:时间戳+帧号
    filename = f"{save_path}/{image.timestamp:.6f}_{image.frame:06d}.png"
    image.save_to_disk(filename)
    print(f"图像已保存:{filename}")

# 在回调中使用
camera.listen(lambda image: save_image(image, "./output/rgb"))
我的习惯:文件名里带上时间戳和帧号,这样后续处理时,可以轻松对齐不同传感器的数据。比如激光雷达和相机,通过帧号就能知道哪一帧是同时采集的。

如果你想保存成其他格式,比如jpg,可以先用上面的image_to_rgb_array转成numpy数组,再用OpenCV保存:

import cv2

def save_as_jpg(image, save_path):
    img_array = image_to_rgb_array(image)
    filename = f"{save_path}/{image.frame:06d}.jpg"
    cv2.imwrite(filename, img_array)
    print(f"JPG图像已保存:{filename}")

4.4 相机畸变模型

说到畸变,很多新手会忽略。但实际项目中,畸变校正很重要。Carla默认的相机是没有畸变的,但你可以手动添加。

为什么要有畸变?因为真实世界的镜头都有畸变,尤其是广角镜头。常见的畸变有两种:

  • 径向畸变:图像边缘向内或向外弯曲
  • 切向畸变:镜头与成像平面不平行导致的变形

Carla里通过carla.CameraActorset_attribute来设置畸变参数:

# 添加畸变参数
camera_bp.set_attribute('lens_circle_multiplier', '3.0')
camera_bp.set_attribute('lens_circle_falloff', '5.0')
camera_bp.set_attribute('lens_k', '-0.15')  # 径向畸变系数
camera_bp.set_attribute('lens_kcube', '0.05')  # 高阶畸变系数

这些参数什么意思?

  • lens_k:主要径向畸变系数。负值产生桶形畸变(边缘向外),正值产生枕形畸变(边缘向内)
  • lens_kcube:高阶畸变,一般设小一点
  • lens_circle_multiplier:暗角效果,值越大边缘越暗
避坑指南:我曾经在项目中直接用了默认的无畸变相机,结果算法在真实场景里表现很差。后来加了轻微的桶形畸变(lens_k=-0.1),模型泛化能力明显提升。所以,建议你至少加一点畸变,让仿真更接近真实。

如果你想模拟特定相机的畸变,可以查一下该相机的标定参数。比如常见的车载摄像头,径向畸变系数一般在-0.1到-0.3之间。

4.5 完整配置示例

最后,给一个完整的相机配置代码。你可以直接拿去用:

import carla
import numpy as np
import cv2

def setup_rgb_camera(world, vehicle):
    # 获取蓝图
    camera_bp = world.get_blueprint_library().find('sensor.camera.rgb')
    
    # 设置参数
    camera_bp.set_attribute('image_size_x', '1280')
    camera_bp.set_attribute('image_size_y', '720')
    camera_bp.set_attribute('fov', '90')
    camera_bp.set_attribute('lens_k', '-0.15')
    camera_bp.set_attribute('lens_kcube', '0.05')
    
    # 设置位置
    camera_transform = carla.Transform(
        carla.Location(x=1.5, z=2.4),
        carla.Rotation(pitch=-15)
    )
    
    # 生成相机
    camera = world.spawn_actor(camera_bp, camera_transform, attach_to=vehicle)
    
    # 定义回调
    def process_image(image):
        # 转numpy数组
        img = np.frombuffer(image.raw_data, dtype=np.uint8)
        img = img.reshape((image.height, image.width, 4))
        img = img[:, :, :3][:, :, ::-1]  # BGRA转RGB
        
        # 保存
        cv2.imwrite(f"output/{image.frame:06d}.png", img)
        print(f"帧 {image.frame} 已保存")
    
    # 开始监听
    camera.listen(process_image)
    return camera

嗯,到这里,RGB相机的配置就讲完了。你可能会觉得参数有点多,但没关系,多用几次就熟了。下一节我们会讲深度相机,那个更有意思。