2. 基于ONNX Runtime的座舱端侧推理引擎集成

任务目标

在车载座舱设备(Linux x86_64或ARM64环境)上集成ONNX Runtime C++ API,加载一个预训练好的ONNX格式模型,对输入张量执行前向推理,并输出分类结果。本案例聚焦于推理引擎的最小可用集成路径,不涉及模型训练与转换细节。

环境与依赖

  • 操作系统:Ubuntu 20.04+(x86_64)或对应ARM64 Linux
  • 编译器:g++ 9.4+(需支持C++17)
  • ONNX Runtime库:从官方Release下载对应架构的预编译包(onnxruntime-linux-x64-1.17.1.tgz 或 onnxruntime-linux-aarch64-1.17.1.tgz)
  • 模型文件:使用ONNX Model Zoo中的MobileNet v2(mobilenetv2-7.onnx),输入为1×3×224×224的RGB图像,输出为1×1000的类别得分

关键完整代码

以下代码实现完整闭环:读取ONNX模型、创建推理会话、构造输入张量、执行推理并输出Top-1类别索引。使用随机输入数据验证推理通路,不依赖真实图像文件,确保最小可复现性。

// onnx_infer.cpp
#include <onnxruntime_cxx_api.h>
#include <vector>
#include <string>
#include <iostream>
#include <cassert>
#include <algorithm>

int main() {
    // 1. 初始化环境
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "cockpit_infer");
    Ort::SessionOptions session_options;
    session_options.SetIntraOpNumThreads(2);
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);

    // 2. 加载模型(请将模型路径替换为实际路径)
    const std::string model_path = "mobilenetv2-7.onnx";
    Ort::Session session(env, model_path.c_str(), session_options);

    // 3. 获取输入输出信息
    Ort::AllocatorWithDefaultOptions allocator;
    auto input_name = session.GetInputNameAllocated(0, allocator);
    auto output_name = session.GetOutputNameAllocated(0, allocator);
    std::vector<const char*> input_names = {input_name.get()};
    std::vector<const char*> output_names = {output_name.get()};

    // 4. 构造输入张量(随机数据模拟预处理后的图像)
    const int64_t batch = 1, channels = 3, height = 224, width = 224;
    const int64_t input_tensor_size = batch * channels * height * width;
    std::vector<float> input_data(input_tensor_size);
    for (auto& v : input_data) v = 0.5f;  // 固定值输入,保证可复现

    std::vector<int64_t> input_shape = {batch, channels, height, width};
    Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
        memory_info, input_data.data(), input_tensor_size,
        input_shape.data(), input_shape.size());

    // 5. 执行推理
    auto output_tensors = session.Run(Ort::RunOptions{nullptr},
                                      input_names.data(), &input_tensor, 1,
                                      output_names.data(), 1);
    assert(output_tensors.size() == 1);

    // 6. 解析输出:取Top-1类别
    float* output_data = output_tensors[0].GetTensorMutableData<float>();
    auto output_info = output_tensors[0].GetTensorTypeAndShapeInfo();
    int64_t output_size = output_info.GetElementCount();
    assert(output_size == 1000);

    int top1_idx = 0;
    for (int64_t i = 1; i < output_size; ++i) {
        if (output_data[i] > output_data[top1_idx]) top1_idx = i;
    }

    std::cout << "推理完成,输出维度: " << output_size << std::endl;
    std::cout << "Top-1 类别索引: " << top1_idx << std::endl;
    std::cout << "Top-1 置信度: " << output_data[top1_idx] << std::endl;
    return 0;
}

编译与运行

# 解压ONNX Runtime库
tar -xzf onnxruntime-linux-x64-1.17.1.tgz
cd onnxruntime-linux-x64-1.17.1

# 编译(假设代码位于上级目录)
g++ -std=c++17 ../onnx_infer.cpp -I include -L lib -lonnxruntime -o onnx_infer

# 设置动态库路径并运行
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
./onnx_infer

验证方法与通过标准

  • 编译通过:g++无报错生成可执行文件。
  • 运行无异常:程序正常退出,不抛出ONNX Runtime错误。
  • 输出合理性:程序打印输出维度为1000,Top-1索引在0~999范围内,置信度为有限浮点数。由于输入为全0.5的固定值,输出结果确定且可复现——每次运行应得到完全相同的Top-1索引与置信度。
  • 集成验证:若替换为真实图像预处理数据(需额外实现图像解码与归一化),Top-1类别应与ImageNet标签对应,但本案例不包含该扩展路径。

常见问题

  • 找不到libonnxruntime.so:确认LD_LIBRARY_PATH指向解压目录的lib文件夹。
  • 模型路径错误:程序启动即报错,请确认mobilenetv2-7.onnx位于当前工作目录或修改代码中的路径。
  • ARM64交叉编译:需下载aarch64版本库,并使用aarch64-linux-gnu-g++编译,运行时在目标板上执行。