(全流程记录)arm设备部署mnn框架并运行YOLO5x
选择合适版本下载,本文中使用的版本为arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu,在x86的Linux平台使用的交叉编译工具。上方是部署到RK3588上的结果,未使用npu,仅cpu跑的结果,本意是想试试部署到树莓派上的效果的,当时没找着树莓派,不过也一样证明了mnn的有效性。需要一些工具,此处先构建MNN的转换工具、测试工具、
一、搭建交叉编译环境(以AArch64 Linux 为目标平台,以YOLOv5为例,操作大致相同)
本文为部署的全流程记录,有不妥之处请狠狠批判😭
需要工具:
sudo apt install cmake git g++ build-essential
(一)、获取交叉编译工具:
在此之前使用
ldd --version查看适配的GCC版本,如果版本不对需要全部重新用对应版本工具链编译
- 在Ubuntu 系统可直接通过apt安装:
sudo apt-get install gcc-aarch64-linux-gnu #apt获取
sudo apt --fix-broken install #如果遇到依赖问题,可以运行该命令修复
-
从源码构建:
(1)下载路径:
选择合适版本下载,本文中使用的版本为arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu,在x86的Linux平台使用的交叉编译工具
(2)下载后解压:
cd 下载后存放的目录/
tar -xvf arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz -C 解压到哪个目录
(3)将工具链加入 PATH:
echo 'export PATH=$工具链目录的位置/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
aarch64-none-linux-gnu-gcc --version #输出类似gcc version 15.2.1 20251203 (Arm GNU Toolchain 15.2.Rel1 (Build arm-15.86)) 等信息证明工具链已正确安装
(二)、获取并编译合适架构的MNN
-
获取MNN源码:
mkdir AArch64_Linux_code && cd AArch64_Linux_code git clone https://github.com/alibaba/MNN.git #大概需要翻墙 #也可通过git clone https://gitee.com/mirrors/mnn.git下载,不需要翻墙对于编译MNN源码对于cmake(3.10 以上)protobuf (3.0 以上)有要求,需要先执行:
sudo apt update sudo apt install protobuf-compiler libprotobuf-dev -
构建MNN框架:
cd /path/to/MNN
./schema/generate.sh
./tools/script/get_model.sh # 这句可选,模型仅demo工程需要,需要先编译出可执行文件(执行步骤三后),目的是得到.mnn文件,电脑上执行这句需要x86编译而非arm编译
-
在/path/to/MNN/CMakeLists.txt下更改编译架构:
加入:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER "交叉编译工具链目录的位置/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc") set(CMAKE_CXX_COMPILER "交叉编译工具链目录的位置/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++")位置如下

-
准备KleidiAI文件:
cd /path/to/MNN git clone https://github.com/ARM-software/kleidiai.git -
编译:
编译宏看:编译宏介绍 — MNN-Doc 2.1.1 documentation
需要一些工具,此处先构建MNN的转换工具、测试工具、性能测试工具、量化工具,按需求看编译宏选择开启
mkdir build && cd build #构建build目录 cmake .. \ -DMNN_BUILD_CONVERTER=ON \ -DMNN_BUILD_TOOLS=ON \ -DMNN_BUILD_BENCHMARK=ON \ -DMNN_BUILD_QUANTOOLS=ON \ -DMNN_EVALUATION=ON \ -DKLEIDIAI_SRC_DIR=/path/to/MNN/kleidiai #kleidiai有的架构可能不支持 make -j8 #选择用几个核进行编译,看自己需要,过多会卡死
cmake命令执行完后观察是否出现如:
-- Found assembler: /home/ykb/Raspberry_PI_code/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/ykb/Raspberry_PI_code/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/ykb/Raspberry_PI_code/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
确认工具链选择正确即可
编译完成后执行:
file libMNN.so
观察到
libMNN.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (GNU/Linux), dynamically linked, with debug_info, not stripped
出现信息表示为arm架构即可
(三)、编译openCV arm架构并部署
下载openCV源码https://opencv.org/releases/

这里选择的是4.8.1版
更改opencv-4.8.1/platforms/linux/aarch64-gnu.toolchain.cmake:
由
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(GNU_MACHINE "aarch64-linux-gnu" CACHE STRING "GNU compiler triple")
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")
改为:
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(CMAKE_C_COMPILER 交叉编译工具链目录的位置/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER 交叉编译工具链目录的位置/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-linux-gnu-g++)
set(GNU_MACHINE "aarch64-linux-gnu" CACHE STRING "GNU compiler triple")
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")
然后在终端中:
cd openCV源码目录\
mkdir build && mkdir arm_openCV_lib
cd build
cmake -DCMAKE_MAKE_PROGRAM:PATH=/usr/bin/make \
-DCMAKE_INSTALL_PREFIX=../arm_openCV_lib \
-DWITH_CUDA=OFF \
-DENABLE_PRECOMPILED_HEADERS=OFF \
-DCMAKE_TOOLCHAIN_FILE=../platforms/linux/aarch64-gnu.toolchain.cmake \
..
观察到
......
-- C++ Compiler: /home/ykb/Raspberry_PI_code/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-g++ (ver 15.2.1)
......
-- C Compiler: /home/ykb/Raspberry_PI_code/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
......
证明成功
然后
make -j8
等待编译
完成后
file bin/opencv_test_core
输出类似bin/opencv_test_core: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, with debug_info, not stripped
表示成功
然后输入:
make install
完成之后将arm_openCV_lib传到板卡上
scp -r arm_openCV_lib 板卡用户名@板卡ip:板卡的目标目录#或者压缩后Windows端用软件传入
#配置库路径
cd /etc/
#看一下有没有ld.so.conf.d目录,没有就创建
#mkdir ld.so.conf.d
cd ld.so.conf.d
sudo touch opencv.conf
sudo vim opencv.conf
#在opencv.conf输入arm_openCV_lib的绝对路径/lib,如/home/root/arm_openCV_lib/lib
sudo ldconfig
#没有报错就说明库的配置完成
(四)、编译pymnn并构建包,需要python时使用(可选)
pymnn是mnn的python API
在当前的build目录
cd ../pymnn/pip_package/
注意:找到MNN/package_scripts/linux/build_whl.sh,根据需要修改其中的内容
重要:对于大部分虚拟机,将31行
cmake $CMAKE_ARGS .. && make MNN MNNTrain MNNConvert MNNOpenCV -j24
改为
cmake $CMAKE_ARGS .. && make MNN MNNTrain MNNConvert MNNOpenCV -j8
不然容易卡死
【Conda】超详细的linux-conda环境安装教程_linux 安装conda-CSDN博客
建议使用Conda,防止冲突,板卡上使用miniConda
python build_deps.py
#python setup.py install --version {MNN版本} #这个是本地构建x86架构并安装,此处为arm架构无法安装
#python setup.py install --version 3.3.1
#目前下载的版本都为3.3.1,具体版本在x86版本的build目录下执行./MNNConvert --version可知
# 这里选择only CPU后端
cd mnn的arm架构根目录/
./package_scripts/linux/build_whl.sh -v 3.3.1 -o MNN-CPU/py_whl
官方示例:Pymnn构建 — MNN-Doc 2.1.1 documentation
#本地安装
cd /path/to/MNN/pymnn/pip_package
python build_deps.py {MNN依赖包组合} #internal,cuda,trt,cuda_tune,opencl,vulkan,render,no_sse,torch,openmp,llm这几个字符串的任意组合,例如字符串可为:"cuda,reder,no_sse"
python setup.py install --version {MNN版本} --deps {MNN依赖包组合}
#构建Python Wheel包
# only CPU后端
./package_scripts/linux/build_whl.sh -v {MNN版本} -o MNN-CPU/py_whl
# CPU+OpenCL后端
./package_scripts/linux/build_whl.sh -v {MNN版本} -o MNN-CPU-OPENCL/py_whl -b
之后把这个包搬到板卡上就行
(五)、转换模型
先构建x86架构的MNN源码:
mkdir x86_MNN_code && cd x86_MNN_code
git clone https://github.com/alibaba/MNN.git #大概需要翻墙
#也可通过git clone https://gitee.com/mirrors/mnn.git下载,不需要翻墙
cd MNN
./schema/generate.sh
mkdir build && cd build #构建build目录
cmake .. \
-DMNN_BUILD_CONVERTER=ON \
-DMNN_BUILD_TOOLS=ON \
-DMNN_BUILD_BENCHMARK=ON \
-DMNN_BUILD_QUANTOOLS=ON \
-DMNN_EVALUATION=ON
make -j8
以YOLO5为例:
1.获取yolo5源码:
git clone https://github.com/ultralytics/yolov5.git
获取.pt文件Release v7.0 - YOLOv5 SOTA Realtime Instance Segmentation · ultralytics/yolov5选一个喜欢的:

这里选择了yolov5x(因为最大),其他的也行,然后丢到yolo目录下
2.转化模型
先从pt转化为onnx:
cd yolo根目录
pip install -r requirements.txt
python export.py --weights=./yolov5x.pt --include=onnx --img=640 --batch=1 --opset=12 --simplify
出现:


从onnx转化为mnn:
cd yolov5
./x86的mnn目录/build/MNNConvert -f ONNX --modelFile yolo根目录/yolov5.onnx --MNNModel ./yolov5.mnn

(六)、部署MNN框架
在板卡上创建目录:
mkdir -p ~/MNN_lib/express
mkdir -p ~/onnxruntime_lib/lib
mkdir ~/test_code
将动态库传到板卡上:
scp -r arm架构MNN目录/MNN/build/libMNN.so 板卡用户名@板卡ip:板卡的目标目录
scp -r arm架构MNN目录/MNN/build/express/libMNN_Express.so 板卡用户名@板卡ip:板卡的目标目录
#例:scp -r /home/ykb/Raspberry_PI_code/MNN/build/libMNN.so linaro@192.168.0.51:~/MNN_lib/
设置MNN动态库:
sudo vim /etc/ld.so.conf.d/MNN_lib.conf
写入库路径,如,即库的绝对路径:
/home/linaro/MNN_lib/
/home/linaro/MNN_lib/express/
然后终端输入:
sudo ldconfig
#没有报错就说明库的配置完成
(七)、交叉编译并运行代码:
在Ubuntu中,执行
mkdir -p test_code/build/
创建文件infer_mnn.cpp:
#include <iostream>
#include <chrono>
#include <opencv2/opencv.hpp>
#include <MNN/Interpreter.hpp>
#include <MNN/ImageProcess.hpp>
using namespace std;
using namespace MNN;
int main() {
// 1. 加载模型
const char* model_path = "yolov5.mnn";
std::shared_ptr<Interpreter> net(Interpreter::createFromFile(model_path));
if (net == nullptr) {
cerr << "无法加载 MNN 模型" << endl;
return -1;
}
// 2. 配置 Session
ScheduleConfig config;
config.type = MNN_FORWARD_CPU; // 在树莓派上使用 CPU
config.numThread = 4; // 树莓派通常是4核
Session* session = net->createSession(config);
// 获取输入输出 Tensor
Tensor* input_tensor = net->getSessionInput(session, nullptr);
// 假设输入尺寸为 640x640,如果模型是动态的,需要 resize
net->resizeTensor(input_tensor, {1, 3, 640, 640});
net->resizeSession(session);
// 3. 图像预处理 (使用 MNN::CV::ImageProcess 加速)
cv::Mat img = cv::imread("data.jpg");
if (img.empty()) {
cerr << "无法读取 data.jpg" << endl;
return -1;
}
// 配置预处理参数 (YOLOv5: RGB, 0-255 -> 0-1, mean=[0,0,0], normal=[1/255, 1/255, 1/255])
CV::ImageProcess::Config process_config;
process_config.filterType = CV::BILINEAR;
// BGR 转 RGB
process_config.sourceFormat = CV::BGR;
process_config.destFormat = CV::RGB;
// Normalization: (x - mean) * normal
::memcpy(process_config.mean, new float[3]{0.0f, 0.0f, 0.0f}, 3 * sizeof(float));
::memcpy(process_config.normal, new float[3]{1.0f/255.0f, 1.0f/255.0f, 1.0f/255.0f}, 3 * sizeof(float));
std::shared_ptr<CV::ImageProcess> pretreat(CV::ImageProcess::create(process_config));
// 执行预处理:从 cv::Mat 转换并拷贝到 MNN input_tensor
// MNN 的 ImageProcess 会自动处理 Resize
pretreat->convert(img.data, 640, 640, 0, input_tensor);
// 4. 执行推理并计时
cout << "开始 MNN 推理..." << endl;
auto start = chrono::high_resolution_clock::now();
net->runSession(session);
auto end = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "MNN 处理时间: " << duration.count() << " ms" << endl;
// 5. 获取输出
Tensor* output_tensor = net->getSessionOutput(session, nullptr);
// 如果输出在 Device 上(例如 GPU),需要拷贝到 Host
std::shared_ptr<Tensor> output_host(new Tensor(output_tensor, Tensor::CAFFE));
output_tensor->copyToHostTensor(output_host.get());
auto shape = output_host->shape();
cout << "推理成功完成,输出维度: ";
for (int s : shape) cout << s << " ";
cout << endl;
// 此处 output_host->host<float>() 即为推理结果数组指针
return 0;
}
创建文件CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
# =========================================================
# 1. 交叉编译配置 (必须在 project() 之前!)
# =========================================================
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
#交叉编译工具链路径
set(TOOLCHAIN_HOME "/home/ykb/Raspberry_PI_code/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu")
set(CMAKE_C_COMPILER "${TOOLCHAIN_HOME}/bin/aarch64-none-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_HOME}/bin/aarch64-none-linux-gnu-g++")
# 强制 cmake 不去宿主机 /usr/lib 找库
set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_HOME})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# =========================================================
# 2. 项目定义
# =========================================================
project(YOLOv5_Inference)
set(CMAKE_CXX_STANDARD 14) # MNN和ORT通常C++14或17都可以
# =========================================================
# 3. 依赖库路径设置
# =========================================================
# --- A. OpenMP (MNN 需要) ---
find_package(OpenMP REQUIRED)
# --- B. OpenCV (ARM64版本) ---
# 这里的路径要指向编译安装的 opencv-4.8.1/build 目录
set(OpenCV_DIR "/home/ykb/Raspberry_PI_code/opencv-4.8.1/build")
find_package(OpenCV REQUIRED)
message(STATUS ">> OpenCV Found: ${OpenCV_DIR}")
# --- C. MNN (ARM64版本) ---
# [TODO] 指向 MNN 根目录
set(MNN_ROOT "/home/ykb/Raspberry_PI_code/MNN")
# MNN 头文件
include_directories(${MNN_ROOT}/include)
# MNN 库文件路径 (假设你是在 build 目录下编译生成的 libMNN.so)
# 如果是静态库可能是 libMNN.a
link_directories(${MNN_ROOT}/build)
# =========================================================
# 4. 编译目标
# =========================================================
# --- 目标 1: MNN 推理 ---
add_executable(yolo_mnn infer_mnn.cpp)
target_link_libraries(yolo_mnn
OpenMP::OpenMP_CXX
MNN
${OpenCV_LIBS}
pthread
dl
)
在终端:
cd build
cmake ..
make
scp 生成的二进制文件名 板卡用户名@板卡ip:板卡的目标目录
#例scp yolo_* linaro@192.168.0.51:/home/linaro/test_code/
scp .mnn文件(之前放在yolo根目录下) 板卡用户名@板卡ip:板卡的目标目录
至此全部完成
(base) linaro@linaro-alip:~/MN_IN/cpp_test$ ./yolo_mnn
CPU Group: [ 0 1 2 3 ], 408000 - 1800000
CPU Group: [ 6 7 ], 408000 - 2256000
CPU Group: [ 4 5 ], 408000 - 2304000
The device supports: i8sdot:1, fp16:1, i8mm: 0, sve2: 0, sme2: 0
开始 MNN 推理...
MNN 处理时间: 1607 ms
推理成功完成,输出维度: 1 25200 85
(base) linaro@linaro-alip:~/MN_IN/cpp_test$ ./yolo_onnx
2026-02-12 10:16:08.298970182 [W:onnxruntime:YOLOv5_ONNX, device_discovery.cc:211 DiscoverDevicesForPlatform] GPU device discovery failed: device_discovery.cc:91 ReadFileContents Failed to open file: "/sys/class/drm/card1/device/vendor"
开始 ONNX 推理...
ONNX 处理时间: 8045 ms
推理成功完成,输出维度: 1x25200x85
实测使用mnn可提高5倍左右的推理速度,相当可观了
上方是部署到RK3588上的结果,未使用npu,仅cpu跑的结果,本意是想试试部署到树莓派上的效果的,当时没找着树莓派,不过也一样证明了mnn的有效性
更多推荐



所有评论(0)