YOLOv8 在单片机上的几种部署方案

news2025/5/20 21:05:34

在这里插入图片描述

YOLOv8 在单片机上的部署方案

单片机资源(如内存、计算能力)有限,直接部署完整的 YOLOv8 模型并不现实。不过,我们可以通过模型量化、优化和使用轻量级框架来实现简化版的目标检测。下面为你介绍几种可行的方案:

方案一:使用 TensorFlow Lite Micro + YOLOv8 简化模型

1. 模型转换与优化

首先在 PC 上对 YOLOv8 进行简化和量化:

import torch
from ultralytics import YOLO
import tensorflow as tf
from onnx_tf.backend import prepare

# 加载 YOLOv8 模型
model = YOLO("yolov8n.pt")  # 使用 Nano 版本

# 导出为 ONNX 格式
model.export(format="onnx", imgsz=(320, 320))  # 减小输入尺寸

# 转换 ONNX 到 TensorFlow
import onnx
onnx_model = onnx.load("yolov8n.onnx")
tf_rep = prepare(onnx_model)
tf_rep.export_graph("yolov8n_tf")

# 转换为 TensorFlow Lite 并应用量化
converter = tf.lite.TFLiteConverter.from_saved_model("yolov8n_tf")
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

# 保存 TFLite 模型
with open("yolov8n_quant.tflite", "wb") as f:
    f.write(tflite_quant_model)
2. 在单片机上部署 TensorFlow Lite Micro

以 Arduino Nano 33 BLE Sense 为例:

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/micro_error_reporter.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "model_data.h"  // 包含量化后的 YOLOv8 模型

// 定义输入输出张量
const int kInputTensorIndex = 0;
const int kOutputTensorIndex = 0;

// 初始化错误报告器
tflite::MicroErrorReporter micro_error_reporter;
const tflite::ErrorReporter* error_reporter = &micro_error_reporter;

// 初始化算子解析器
tflite::AllOpsResolver resolver;

// 加载模型
const tflite::FlatBufferModel* model = 
    tflite::FlatBufferModel::BuildFromBuffer(model_data, model_data_len);

// 创建解释器
constexpr int tensor_arena_size = 136 * 1024;
uint8_t tensor_arena[tensor_arena_size];
tflite::SimpleTensorAllocator tensor_allocator(tensor_arena, tensor_arena_size);
tflite::MicroInterpreter interpreter(model, resolver, &tensor_allocator, error_reporter);

// 分配张量
TfLiteStatus allocate_status = interpreter.AllocateTensors();
if (allocate_status != kTfLiteOk) {
  Serial.println("Failed to allocate tensors!");
  return;
}

// 获取输入输出张量
TfLiteTensor* input_tensor = interpreter.input(kInputTensorIndex);
TfLiteTensor* output_tensor = interpreter.output(kOutputTensorIndex);

// 图像预处理函数(示例)
void preprocess_image(uint8_t* image_data, float* input_data) {
  // 调整图像大小为模型输入尺寸 (320x320)
  // 归一化像素值到 [0, 1] 或 [-1, 1]
  // ...
}

// 后处理函数(简化版 NMS)
void postprocess(float* output_data, int width, int height) {
  // 解析模型输出,提取边界框、类别和置信度
  // 应用非极大值抑制(NMS)
  // ...
}

void setup() {
  Serial.begin(115200);
  // 初始化摄像头
  // ...
}

void loop() {
  // 捕获图像
  uint8_t* image_data = capture_image();
  
  // 预处理图像
  preprocess_image(image_data, input_tensor->data.f);
  
  // 运行推理
  TfLiteStatus invoke_status = interpreter.Invoke();
  if (invoke_status != kTfLiteOk) {
    Serial.println("Failed to invoke interpreter!");
    return;
  }
  
  // 后处理结果
  postprocess(output_tensor->data.f, 320, 320);
  
  // 显示或发送结果
  // ...
  
  delay(100);
}

方案二:使用 TinyML 框架(如 NCNN)

NCNN 是专为移动设备优化的轻量级神经网络推理框架,非常适合单片机:

1. 模型转换

将 YOLOv8 转换为 NCNN 格式:

# 首先将 YOLOv8 导出为 ONNX
yolo export model=yolov8n.pt format=onnx imgsz=320

# 使用 onnx2ncnn 工具转换为 NCNN 格式
onnx2ncnn yolov8n.onnx yolov8n.param yolov8n.bin

# 优化模型
ncnnoptimize yolov8n.param yolov8n.bin yolov8n-opt.param yolov8n-opt.bin 1
2. 在单片机上集成 NCNN

以下是一个简化的 NCNN 集成示例:

#include "net.h"
#include "benchmark.h"
#include "mat.h"

// 初始化网络
ncnn::Net yolov8;
yolov8.load_param("yolov8n-opt.param");
yolov8.load_model("yolov8n-opt.bin");

// 目标检测函数
std::vector<Object> detect_yolov8(const cv::Mat& bgr, float prob_threshold = 0.25f, float nms_threshold = 0.45f)
{
    int img_w = bgr.cols;
    int img_h = bgr.rows;

    // 图像预处理
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 320, 320);
    
    // 归一化
    const float mean_vals[3] = {0.f, 0.f, 0.f};
    const float norm_vals[3] = {1/255.f, 1/255.f, 1/255.f};
    in.substract_mean_normalize(mean_vals, norm_vals);

    // 运行推理
    ncnn::Extractor ex = yolov8.create_extractor();
    ex.set_num_threads(2);
    ex.input("images", in);

    ncnn::Mat out;
    ex.extract("output", out);

    // 后处理
    std::vector<Object> objects;
    // ... 解析输出并应用 NMS
    
    return objects;
}

void setup() {
  // 初始化串口和摄像头
}

void loop() {
  // 捕获图像
  cv::Mat image = capture_image();
  
  // 检测目标
  std::vector<Object> objects = detect_yolov8(image);
  
  // 处理检测结果
  // ...
  
  delay(100);
}

方案三:使用 YOLO-NAS Tiny

YOLO-NAS 是一种较新的轻量级目标检测模型,性能优于 YOLOv5/YOLOv8 的 Nano 版本:

# 安装 super-gradients
pip install super-gradients

# 导出 YOLO-NAS Tiny 为 ONNX
from super_gradients.training import models

# 加载模型
model = models.get("yolo_nas_s", pretrained_weights="coco")

# 导出为 ONNX
model.export("yolo_nas_s.onnx", input_shape=(3, 320, 320))

# 然后按照上述方法将 ONNX 转换为适合单片机的格式

资源限制与优化建议

  1. 模型选择:优先选用 Nano 或 Tiny 版本的模型
  2. 输入尺寸:使用较小的输入尺寸(如 160×160 或 320×320)
  3. 量化:使用 8 位或 16 位量化,甚至二值化
  4. 层融合:利用框架的层融合功能减少计算量
  5. 算法简化:只检测关键类别,降低模型复杂度

对于资源极其有限的单片机(如 Arduino Uno),可能需要使用更轻量级的算法,如 Tiny YOLO 或专门为 MCU 设计的目标检测模型。

常见问题及解决办法

CUDA 相关问题:

要保证你的 GPU 驱动版本与 CUDA 版本兼容
可以使用 nvidia-smi 命令查看 GPU 信息

显示问题:

如果你在服务器上运行,可能会遇到无法显示图像的问题,这时可以加上 save=True 参数将结果保存下来。

依赖冲突问题:

可以尝试在全新的虚拟环境中重新安装所有依赖。
如果在部署过程中遇到特定问题,请提供详细的错误信息,以便进一步排查。

优化建议与注意事项

模型压缩策略:

1、使用 YOLOv8 Nano 或定制更小的模型
2、降低输入分辨率(128×128 或 160×160)
3、应用 INT8 或二值化量化
4、裁剪不重要的层

硬件选择指南:

1、普通任务:STM32H7 系列(带 DSP/FPU)
2、高性能需求:Kendryte K210、Nordic nRF9160
3、预算充足:Raspberry Pi Zero 2W + Edge TPU

实际性能参考:

1、STM32H747:约 0.2 FPS(160×160 输入)
2、Kendryte K210:约 5 FPS(160×160 输入)
3、Raspberry Pi Zero 2W + Edge TPU:约 15 FPS(320×320 输入)

对于资源极其有限的单片机(如 Arduino Uno),建议仅处理预处理任务(如图像缩放),并将数据发送到外部设备进行推理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

后端框架(1):Mybatis

什么是框架&#xff1f; 盖高楼&#xff0c;框架结构。 框架结构就是高楼的主体&#xff0c;基础功能。 把很多基础功能已经实现了(封装了)。 在基础语言之上&#xff0c;对各种基础功能进行封装&#xff0c;方便开发者&#xff0c;提高开发效率。 mybatis&#xff1a;对jd…

linux下tcp/ip网络通信笔记1,

本文章主要为博主在学习网络通信的笔记一个Udp_echo_server,和client的代码实现 1&#xff0c;网络发展&#xff0c;网络协议&#xff0c;意识到网络通信——不同主机的进程间通信&#xff0c; 2&#xff0c;学习如何在应用层调用系统提供的接口进行通信&#xff0c;echo_Udp…

语音识别——声纹识别

通过将说话人的声音与数据库中的记录声音进行比对&#xff0c;判断说话人是否为数据库白名单中的同一人&#xff0c;从而完成语音验证。目前&#xff0c;3D-Speaker 声纹验证的效果较为出色。 3D-Speaker 是一个开源工具包&#xff0c;可用于单模态和多模态的说话人验证、说话…

window 显示驱动开发-报告图形内存(三)

图形内存报告示例 示例 1&#xff1a;笔记本电脑上的 128 MB 专用板载图形内存 以下屏幕截图显示了使用 Intel Iris 离散图形适配器运行 Windows 11 的 Surface 笔记本电脑的计算图形内存数。 适配器的可用内存总数为 16424 MB&#xff0c;用于图形用途&#xff0c;细分如下&…

UE5 GAS框架解析内部数据处理机制——服务器与客户端

当&#xff0c; gas通过点击鼠标光标触发事件时&#xff0c;内部的处理机制。 当通过点击事件&#xff0c;命中中目标时&#xff0c; 可获取到对应的TargetData 目标数据。处理相应的操作。 仅有本地的客户端的情况下。命中并不会有什么异常。 当存在服务器时&#xff0c; 服…

开源GPU架构RISC-V VCIX的深度学习潜力测试:从RTL仿真到MNIST实战

点击 “AladdinEdu&#xff0c;同学们用得起的【H卡】算力平台”&#xff0c;H卡级别算力&#xff0c;按量计费&#xff0c;灵活弹性&#xff0c;顶级配置&#xff0c;学生专属优惠。 一、开篇&#xff1a;AI芯片架构演变的三重挑战 &#xff08;引述TPUv4采用RISC-V的行业案…

Android Coli 3 ImageView load two suit Bitmap thumb and formal,Kotlin(七)

Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;七&#xff09; 在 Android Coli 3 ImageView load two suit Bitmap thumb and formal&#xff0c;Kotlin&#xff08;六&#xff09;-CSDN博客 的基础上改进&#xff0c;主要是…

【工具】Windows|外接的显示器怎么用软件调亮度(Brightness Slider)

文章目录 工具安装及使用Twinkle Tray&#xff1a;Brightness Slider补充背景知识1. DDC/CI&#xff08;Display Data Channel Command Interface&#xff09;2. WMI&#xff08;Windows Management Instrumentation&#xff09;3. Twinkle Tray如何结合两者&#xff1f;对比总…

【Nextcloud】使用 LNMP 架构搭建私有云存储:Nextcloud 实战指南

目录 一、环境准备与基础配置 1. 系统环境要求 2. 初始化系统配置 二、搭建 LNMP 基础架构 1. 一键安装 LNMP 组件 2. 启动数据库服务 三、部署 Nextcloud 存储服务 1. 上传并解压安装包 2. 设置目录权限&#xff08;测试环境配置&#xff09; 3. 配置 MariaDB 数据库…

【办公类-100-01】20250515手机导出教学照片,自动上传csdn+最小化Vscode界面

背景说明&#xff1a; 每次把教学照片上传csdn&#xff0c;都需要打开相册&#xff0c;一张张截图&#xff0c;然后ctrlV黏贴到CSDN内&#xff0c;我觉得太烦了。 改进思路&#xff1a; 是否可以先把所有照片都上传到csdn&#xff0c;然后再一张张的截图&#xff08;去掉幼儿…

uniapp-商城-60-后台 新增商品(属性的选中和页面显示,数组join 的使用)

前面添加了属性&#xff0c;添加属性的子级项目。也分析了如何回显&#xff0c;但是在添加新的商品的时&#xff0c;我们也同样需要进行选择&#xff0c;还要能正常的显示在界面上。下面对页面的显示进行分析。 1、界面情况回顾 属性显示其实是个一嵌套的数据显示。 2、选中的…

[c语言日寄]数据结构:栈

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

WEB安全--Java安全--LazyMap_CC1利用链

一、前言 该篇是基于WEB安全--Java安全--CC1利用链-CSDN博客的补充&#xff0c;上篇文章利用的是TransformedMap类&#xff0c;而CC链的原作者是利用的LazyMap类作为介质进行的触发。 所以本文将分析国外原作者在ysoserial commonscollections1中给出的CC1利用链。 二、回顾梳…

黑马k8s(六)

1.Deployment&#xff08;Pod控制器&#xff09; Selector runnginx 标签选择&#xff1a;会找pod打的标签 执行删除之后&#xff0c;pod也会删除&#xff0c;Terminating正在删除 如果想要访问其中的一个pod借助&#xff1a;IP地址端口号访问 假设在某一个瞬间&#xff0c;…

【OpenGL学习】(一)创建窗口

文章目录 【OpenGL学习】&#xff08;一&#xff09;创建窗口 【OpenGL学习】&#xff08;一&#xff09;创建窗口 GLFW OpenGL 本身只是一套图形渲染 API&#xff0c;不提供窗口创建、上下文管理或输入处理的功能。 GLFW 是一个支持创建窗口、处理键盘鼠标输入和管理 OpenGL…

AI大语言模型评测体系演进与未来展望

随着人工智能技术的飞速发展,大语言模型(LLMs)已成为自然语言处理领域的核心研究方向。2025年最新行业报告显示,当前主流模型的评测体系已从单一任务评估转向多维度、全链路的能力剖析。例如,《全球首个大语言模型意识水平”识商”白盒DIKWP测评报告》通过数据、信息、知识…

微服务项目->在线oj系统(Java版 - 5)

相信自己,终会成功 微服务代码: lyyy-oj: 微服务 目录 C端代码 用户题目接口 修改后用户提交代码(应用版) 用户提交题目判题结果 代码沙箱 1. 代码沙箱的核心功能 2. 常见的代码沙箱实现方式 3. 代码沙箱的关键问题与解决方案 4. 你的代码如何与沙箱交互&#xff1f; …

get请求使用数组进行传参

get请求使用数组进行传参,无需添加中括号 mvc接口要添加参数名&#xff0c;使用array承接。不能用list, 否则会报错 这里是用apifox模拟前端调用。 前端调用代码 // 根据项目ID和角色ID查询相关审批人 export function findRelativeApproverByProjectIdAndRoleId(roleIds, p…

【MySQL成神之路】MySQL常用语法总结

目录 MySQL 语法总结 数据库操作 表操作 数据操作 查询语句 索引操作 约束 事务控制 视图操作 存储过程和函数 触发器 用户和权限管理 数据库操作 创建数据库&#xff1a; CREATE DATABASE database_name; 选择数据库&#xff1a; USE database_name; 删除数…

Linux动静态库制作与原理

什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统…