4. 边缘AI视觉系统软件栈

在 i.MX 95 上做边缘 AI 视觉,软件栈不是一条直线,而是一条从摄像头传感器一直延伸到 NPU 推理结果的纵向链路。每一层都有明确的职责边界:内核驱动负责把硬件寄存器变成标准设备节点,V4L2 负责把设备节点变成可编程的视频采集接口,GStreamer 负责把采集、编码、显示和推理串成可动态重组的管线,OpenCV/OpenCL 负责在 CPU 与 GPU 之间分配通用视觉算子,NPU 运行时与 eIQ 工具链则负责把训练好的模型真正跑在 Ethos-U 或 Neutron 加速器上。理解这条链路的层次关系,比记住某个 API 的调用顺序更重要。

内核驱动层:设备树与媒体控制器

i.MX 95 的视觉输入通常来自 MIPI CSI-2 摄像头。内核侧需要三部分配合:CSI 接收控制器驱动(如 imx95-mipi-csi)、图像处理单元驱动(ISP 或 imx-media 子设备)、以及传感器自身的 I2C 驱动(如 ov5640、imx219)。这些驱动通过 V4L2 子设备(subdev)模型注册,并由 media controller 框架统一管理。

设备树中需要描述 CSI 端口、I2C 地址、时钟、复位 GPIO 以及数据通道。一个典型的片段如下:

&csi {
    status = "okay";
    port {
        csi_in: endpoint {
            remote-endpoint = <&ov5640_out>;
            data-lanes = <1 2>;
            clock-lanes = <0>;
        };
    };
};

&i2c3 {
    ov5640: camera@3c {
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        csi_id = <0>;
        port {
            ov5640_out: endpoint {
                remote-endpoint = <&csi_in>;
                data-lanes = <1 2>;
            };
        };
    };
};

加载后可用 media-ctl -p 查看拓扑,确认 sensor → CSI → ISP → video 节点的链路是否完整。若链路断开,/dev/video0 可能仍存在但无法出流,这是排障时首先要检查的点。

V4L2 采集框架:从设备节点到缓冲区

V4L2 是 Linux 视频采集的标准接口。在 i.MX 95 上,采集流程通常包括:打开 /dev/video0、VIDIOC_S_FMT 设置分辨率与像素格式(如 V4L2_PIX_FMT_YUYV 或 V4L2_PIX_FMT_NV12)、VIDIOC_REQBUFS 申请缓冲区、mmap 映射、VIDIOC_QBUF 入队、VIDIOC_STREAMON 启动,然后循环 VIDIOC_DQBUF 取帧、处理、再 QBUF 回队。这个“入队—出队—回队”的循环是零拷贝采集的核心。

实际工程中更常用 v4l2-ctl 快速验证:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12
v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=30 --stream-to=frame.raw

若 30 帧能稳定落盘且无丢帧,说明 CSI 链路、DMA 和缓冲区管理基本正常。此时再进入 GStreamer 层,问题定位会清晰很多。

GStreamer 管线:把采集、显示与推理串起来

GStreamer 的价值在于用插件拼装管线,并支持动态重配置。一个典型的 i.MX 95 预览管线如下:

gst-launch-1.0 v4l2src device=/dev/video0 ! \
  video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
  imxvideoconvert_g2d ! waylandsink

若要在管线中插入 AI 推理,通常使用 appsink 或 tee 分流:一路送显示,一路送推理。例如用 tee 分出 queue 后接 appsink,在应用层取帧送入 OpenCV 或 NPU。这样显示与推理解耦,避免推理耗时拖慢预览帧率。

OpenCV 与 OpenCL:通用视觉算子的落点

OpenCV 在 i.MX 95 上主要承担预处理与后处理:颜色空间转换、缩放、ROI 裁剪、阈值、轮廓、NMS 等。若编译时启用 OpenCL,部分算子可下沉到 GPU。判断是否值得用 OpenCL,可以看单帧耗时:若 cv::resize 或 cv::cvtColor 在 CPU 上占用超过 5 ms,且 GPU 空闲,就值得尝试 cv::UMat 路径。但要注意,小分辨率下 GPU 传输开销可能反而更慢,需实测对比。

NPU 运行时与 eIQ 工具链

i.MX 95 的 NPU 推理依赖 NXP eIQ 工具链。典型流程是:在 PC 端用 eIQ Toolkit 或 eiq-converter 将 ONNX/TFLite 模型转换为 NPU 可执行的 .tflite 或 .nb 格式,量化到 int8,再部署到板端。板端运行时通过 libethosu 或 Neutron SDK 加载模型,输入输出张量通常以 mmap 或 DMA-BUF 方式与 V4L2 缓冲区对接,避免额外拷贝。

一个可参考的量化检查点是:MobileNetV2 在 224×224 输入下,int8 量化后模型大小约 3.5 MB,单帧推理在 NPU 上通常可控制在 10 ms 以内;若明显偏慢,应检查是否误用了 CPU 回退路径,或输入张量未对齐。

i.MX 95 边缘AI视觉软件栈纵向链路 从传感器到NPU推理结果的分层职责与数据流 MIPI CSI-2 传感器 OV5640 / IMX219 内核驱动层 CSI / ISP / I2C subdev V4L2 采集 /dev/video0 · mmap GStreamer tee / appsink OpenCV + OpenCL 预处理/后处理 cvtColor · resize · ROI · NMS · UMat(GPU) 取帧 appsink NPU 运行时 + eIQ 工具链 int8量化 · .tflite/.nb · Ethos-U / Neutron 张量输入 推理结果:类别 / 置信度 / 检测框 回传应用层,叠加显示或触发控制 关键检查点 media-ctl -p 拓扑 v4l2-ctl 出流验证 性能判断依据 预处理 < 5 ms NPU 单帧 < 10 ms 数据流方向:传感器 → 内核 → V4L2 → GStreamer/OpenCV → NPU → 结果回传

分层排障与性能判断

当系统不出图或推理异常时,按层排查比盲目改代码有效。第一层看 dmesg | grep -i csi 是否有链路锁定;第二层用 v4l2-ctl --stream-mmap 确认能否出流;第三层用 GST_DEBUG=3 看管线是否协商成功;第四层用 opencv_version --opencl 确认 OpenCL 可用;第五层用 eIQ 的 benchmark 工具确认 NPU 是否真正参与推理。每一层都有独立的成功判据,逐层通过后再看端到端延迟,才能定位真正的瓶颈。