1. 从零开始为什么要在行空板上跑YOLOv8n最近在折腾边缘计算项目手头正好有一块行空板就琢磨着能不能把最新的YOLOv8模型给跑起来。你可能要问市面上那么多开发板为什么偏偏选行空板答案很简单它本质上是一台内置了Python环境的微型Linux电脑自带屏幕、Wi-Fi和丰富的传感器接口对于想快速验证一个视觉AI应用原型的人来说它几乎是个“开箱即用”的解决方案。你不需要额外接显示器、键盘鼠标甚至不需要复杂的系统烧录连上网络就能开始编程。而YOLOv8作为Ultralytics公司推出的最新目标检测模型以其在精度和速度上的优秀平衡迅速成为了工业界和爱好者的新宠。尤其是它的nano版本YOLOv8n模型体积小计算量相对友好是边缘部署的首选。把YOLOv8n部署到行空板上意味着你可以亲手打造一个能“看懂”周围世界的智能终端——无论是做一个能识别宠物的智能摄像头还是一个能分拣零件的简易机械臂视觉系统这个组合都能提供一个非常不错的起点。当然这个过程绝非一帆风顺。行空板基于ARM架构算力有限内存也不大直接运行为x86架构和强大GPU优化的PyTorch模型会遇到各种兼容性和性能上的“坑”。但正是踩过这些坑才能深刻理解从模型训练到边缘部署的完整链路。接下来我就把从环境准备、模型转换、代码编写到性能优化的完整过程以及我遇到的那些“坑”和解决方案毫无保留地分享给你。2. 行空板环境准备避开依赖冲突的“雷区”行空板默认运行的是基于Debian的定制Linux系统并预装了Python 3.9。这听起来是个不错的开始但预装环境往往是为了兼容其自带的图形化编程界面对于跑深度学习模型所需的库版本可能并不匹配。我的建议是不要动系统自带的Python环境。我们新建一个独立的虚拟环境这样既能保证项目依赖的纯净也避免了把系统搞崩的风险。2.1 创建并激活虚拟环境通过SSH或者行空板的Web终端通常地址是http://行空板IP:8888连接到板子。首先我们创建一个名为yolov8_env的虚拟环境。python3 -m venv yolov8_env source yolov8_env/bin/activate激活后命令行提示符前会出现(yolov8_env)字样这表示我们已经进入了这个独立的环境。2.2 安装PyTorch选择ARM兼容版本这是最关键也最容易出错的一步。PyTorch官方为ARM架构的Linux比如树莓派提供了预编译的轮子wheel但行空板的CPU架构也是ARM我们可以借鉴。直接使用pip install torch会默认安装x86版本肯定无法运行。我们需要找到正确的安装命令。访问PyTorch官网的安装页面选择以下配置PyTorch Build: Stable (1.13.1)Your OS: LinuxPackage: PipLanguage: PythonCompute Platform: CPU官网生成的命令可能是pip3 install torch torchvision torchaudio。但对于ARM设备我们需要一个明确的、针对ARM架构的轮子地址。经过多次测试以下命令在行空板上是稳定可用的pip install torch1.13.1 torchvision0.14.1 --extra-index-url https://download.pytorch.org/whl/cpu注意这里必须指定版本。最新版的PyTorch可能没有适配的ARM预编译包会导致编译失败或运行时出现非法指令错误。1.13.1和0.14.1这个组合是经过验证的稳定版本。安装过程会比较慢因为需要下载适配ARM的较大文件包请耐心等待。2.3 安装Ultralytics YOLOv8和其他依赖虚拟环境下安装YOLOv8的官方库就简单了pip install ultralytics这个命令会自动安装ultralytics库以及其依赖如opencv-python-headless无GUI界面的OpenCV更适合服务器环境、matplotlib、pandas等。opencv-python-headless比完整的opencv-python体积更小依赖更少非常适合行空板这样的环境。为了后续可能用到的图像处理和数据操作我们也可以一并安装一些常用库pip install numpy pillow全部安装完成后可以通过pip list查看已安装的包确认torch,ultralytics等核心库都已就位。3. 模型获取与转换从PyTorch到板载可用的格式环境准备好了接下来是模型。虽然我们可以直接从Ultralytics的库中在线加载预训练的YOLOv8n模型但对于边缘设备我更推荐预先下载并转换为最适合的格式。这有两个好处一是避免每次运行时重复下载二是可以尝试一些优化格式以提升速度。3.1 方案一直接使用PyTorch模型.pt这是最直接的方式。你可以先在个人电脑需要有GPU的环境上运行以下代码将模型下载下来from ultralytics import YOLO model YOLO(yolov8n.pt) # 这会触发下载 # 简单地推理一次确保模型正确 results model(https://ultralytics.com/images/bus.jpg)下载后的yolov8n.pt文件大约12MB。然后通过SCP如使用WinSCP工具或行空板Web界面提供的文件上传功能将这个.pt文件传输到行空板的项目目录中。在行空板上你就可以直接加载这个本地文件from ultralytics import YOLO model YOLO(path/to/your/yolov8n.pt)优点简单无需转换保持原汁原味的PyTorch模型兼容性最好。缺点加载速度相对较慢因为PyTorch需要动态解析模型结构运行时也并非最优。3.2 方案二转换为ONNX格式并尝试优化ONNX是一种开放的模型格式旨在让模型在不同框架间迁移。虽然行空板上直接运行ONNX需要额外的运行时如onnxruntime但有时它能获得比原生PyTorch更优的推理速度尤其是配合一些针对ARM的优化时。转换同样建议在电脑上进行from ultralytics import YOLO model YOLO(yolov8n.pt) model.export(formatonnx, simplifyTrue, opset12) # 导出为ONNX并进行简化simplifyTrue会应用ONNX-Simplifier对计算图进行优化移除不必要的操作这对边缘设备很重要。导出的文件是yolov8n.onnx。将.onnx文件传到行空板后需要安装ONNX Runtime。注意要安装ARM兼容版本pip install onnxruntime在行空板上使用ONNX模型进行推理就不能直接用ultralytics的高级API了需要自己写预处理和后处理代码这增加了复杂性。除非你对性能有极致要求并且愿意折腾否则对于初次尝试我强烈建议使用方案一PyTorch .pt格式它的易用性和Ultralytics提供的丰富接口如绘制检测框能让你快速看到效果建立信心。4. 编写推理脚本让模型在行空板上“动”起来有了模型我们来写一个完整的推理脚本。这个脚本将实现1从摄像头实时捕获画面2对每一帧运行YOLOv8n推理3在画面上绘制检测框和标签4在行空板的自带屏幕上显示结果。在行空板的工作目录下创建一个Python文件比如run_yolov8.py。4.1 导入必要的库import cv2 from ultralytics import YOLO import time4.2 加载模型与初始化摄像头# 加载模型指定本地模型文件路径 model YOLO(yolov8n.pt) # 确保yolov8n.pt文件在当前目录或指定路径 # 初始化摄像头行空板上的摄像头设备号通常是0 cap cv2.VideoCapture(0) # 设置一个较低的分辨率以减轻处理压力320x240是一个不错的起点 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) # 检查摄像头是否成功打开 if not cap.isOpened(): print(错误无法打开摄像头。) exit()这里将摄像头分辨率设为320x240是权衡速度和精度的关键。分辨率越高细节越多但需要处理的像素呈平方增长会显著降低帧率。对于YOLOv8n这样的小模型输入图像在推理前会被自动缩放到640x640默认所以过高的原始分辨率意义不大反而增加了前期处理开销。4.3 主循环捕获、推理、显示print(开始运行YOLOv8n实时检测按 q 键退出。) try: while True: # 记录帧开始时间用于计算FPS start_time time.time() # 从摄像头读取一帧 ret, frame cap.read() if not ret: print(无法从摄像头获取帧退出。) break # 使用YOLOv8进行推理 # streamTrue 参数用于处理视频流时更高效 results model(frame, streamTrue) # 遍历结果对于单张图片其实只有一个结果 for r in results: # 在原始帧上绘制检测结果框、标签、置信度 annotated_frame r.plot() # 在屏幕上显示带标注的帧 cv2.imshow(YOLOv8n on UniHiker, annotated_frame) # 计算并打印当前FPS fps 1 / (time.time() - start_time) print(f当前FPS: {fps:.2f}, end\r) # \r让输出在同一行刷新 # 如果按下q键则退出循环 if cv2.waitKey(1) 0xFF ord(q): break finally: # 释放摄像头资源并关闭所有OpenCV窗口 cap.release() cv2.destroyAllWindows() print(\n程序已退出。)这段代码的核心是model(frame, streamTrue)和r.plot()。streamTrue是针对视频流的一个优化选项。r.plot()是Ultralytics提供的一个非常方便的方法它直接将检测框、类别名和置信度绘制在图像上返回一个已经画好的NumPy数组图像省去了我们手动绘制的麻烦。4.4 首次运行的常见问题与解决运行这个脚本你可能会遇到两个典型问题ImportError: libGL.so.1错误这是因为OpenCV需要一些图形库。行空板预装的是opencv-python-headless但cv2.imshow需要图形支持。解决方法是安装必要的系统库sudo apt-get update sudo apt-get install libgl1-mesa-glx安装后再次运行脚本。帧率极低低于1 FPS这是正常现象。在默认设置下未经优化的YOLOv8n在行空板的ARM CPU上运行就是这个速度。别担心下一章我们就来解决性能问题。5. 性能优化实战从“幻灯片”到“可用的”实时检测让模型跑起来只是第一步让它跑得“流畅”才是真正的挑战。在行空板上我们需要从多个层面进行“瘦身”和优化。5.1 模型推理参数调优Ultralytics的model.predict()或直接调用模型时有许多参数可以显著影响速度results model(frame, streamTrue, imgsz320, conf0.5, iou0.5, halfFalse, devicecpu)imgsz图像尺寸这是最有效的提速杠杆。模型默认输入尺寸是640。将其减半到320计算量会减少到原来的约1/4。代价是检测小目标的能力会下降。对于近距离、目标较大的场景如桌面物体识别320甚至224都是可行的。你可以根据你的应用场景调整。conf置信度阈值默认0.25。提高它如0.5可以过滤掉大量低置信度的预测框减少后续的非极大值抑制NMS计算量也能让结果更干净。iouNMS的IoU阈值默认0.7。对于重叠框的抑制阈值。如果你的场景中目标重叠不多可以适当调高如0.8减少NMS操作。half半精度推理设置为True可以使用FP16半精度。但在行空板的CPU上halfTrue通常不会加速甚至可能更慢因为CPU对FP16计算没有特殊优化反而增加了类型转换开销。这个参数主要针对GPU。device明确指定devicecpu。优化后的推理调用示例# 针对行空板的优化参数组合 results model(frame, streamTrue, imgsz320, conf0.6, iou0.7, devicecpu)仅通过调整imgsz和conf我就将FPS从不到1提升到了2-3效果立竿见影。5.2 图像预处理与后处理的优化我们自己的代码也有优化空间跳过不必要的绘制如果只是做逻辑判断比如检测到“猫”就触发一个信号可以完全不用r.plot()这个相对耗时的绘图函数直接访问results[0].boxes数据即可。降低显示帧率人眼对流畅度的感知有限。我们可以每推理2-3帧才更新一次屏幕显示。这能显著减少cv2.imshow()的调用开销。display_counter 0 display_every_n_frames 2 # 每2帧显示一次 # 在主循环内... if display_counter % display_every_n_frames 0: cv2.imshow(YOLOv8n on UniHiker, annotated_frame) display_counter 1使用cv2.imshow的优化对于行空板自带的屏幕确保OpenCV窗口创建一次即可避免在循环中重复创建。5.3 系统级优化思路如果经过上述代码优化性能仍不满足需求可以考虑更底层的方案使用TensorRT或OpenVINO这是性能提升的“大招”。NVIDIA的TensorRT需要Nano等带GPU的板子和Intel的OpenVINO都能对模型进行深度优化、层融合、精度校准在支持硬件上带来数倍甚至数十倍的提升。但行空板以Sipeed Maix系列为例的CPU并非Intel或NVIDIA高端产品OpenVINO的ARM版本支持有限TensorRT则不适用。这条路对行空板比较困难。尝试其他轻量级运行时如前文提到的ONNX Runtime它针对不同硬件有优化执行提供器。可以尝试安装onnxruntime的ARM版本并测试其推理速度是否优于原生PyTorch。模型蒸馏或剪枝这是更上游的优化。使用更小的自定义模型或者对YOLOv8n进行剪枝移除冗余的神经元或通道得到一个更小的模型。但这需要重新训练或微调模型门槛较高。对于大多数入门和原型开发场景将imgsz设为320并调整置信度阈值已经能在行空板上获得3-5 FPS的“可观察”实时性能这对于很多非高速运动的检测场景如安防监控、物品盘点已经足够。6. 项目进阶将检测结果融入实际应用模型能实时检测并显示我们已经完成了“眼睛”的部分。接下来我们要让行空板根据“看到”的东西做出“反应”。这才是边缘AI项目的精髓。6.1 示例检测到特定物体后发出声音或点亮LED假设我们的项目是“智能门禁”检测到“人”person时行空板播放一个欢迎音效同时点亮板载的LED灯。首先我们需要解析检测结果。results[0].boxes包含了所有检测框的信息。# 在主循环的推理部分之后 for r in results: boxes r.boxes if boxes is not None: # 确保检测到了物体 # 获取所有检测到的类别ID cls_ids boxes.cls.cpu().numpy().astype(int) # 获取所有检测到的类别名称 cls_names [model.names[i] for i in cls_ids] # 检查是否有“人” if person in cls_names: print(检测到人) # 这里可以触发你的动作例如 # 1. 控制LED假设使用行空板GPIO库 # from unihiker import GUI, Audio # gui GUI() # gui.draw_led(x120, y20, colorred) # 点亮一个红色LED图案软件模拟 # 2. 播放声音需确保音频文件存在 # audio Audio() # audio.play_wav(welcome.wav)注意行空板的具体硬件控制API可能因型号和固件版本而异。上述unihiker库的用法仅为示例请参考行空板官方文档来操作真实的GPIO、LED或播放音频。6.2 示例将检测结果通过网络发送另一个常见需求是将检测结果如物体类别、位置、数量发送到远程服务器或手机APP。我们可以使用行空板自带的Wi-Fi模块。import requests import json # 在主循环中处理完results后 detection_data { timestamp: time.time(), objects_detected: [] } for r in results: boxes r.boxes if boxes is not None: for box, cls_id in zip(boxes.xyxy, boxes.cls): # box是[x1, y1, x2, y2]格式的坐标 obj_info { class: model.names[int(cls_id)], bbox: box.cpu().numpy().tolist(), confidence: float(boxes.conf[0]) # 简化处理取第一个置信度 } detection_data[objects_detected].append(obj_info) # 将数据以JSON格式POST到服务器 try: response requests.post(http://你的服务器地址/api/detect, jsondetection_data, timeout2) if response.status_code 200: print(数据发送成功) except requests.exceptions.RequestException as e: print(f网络发送失败: {e})这样你的行空板就变成了一个智能感知节点将视觉信息转化为了可远程监控的网络数据。6.3 长期运行与稳定性考虑如果你希望这个程序像守护进程一样长期运行还需要考虑异常处理与自动重启使用try...except包裹主循环捕获意外错误如摄像头断开、内存不足并记录日志。可以考虑用系统服务如systemd来管理脚本崩溃后自动重启。内存管理长时间运行后Python可能会产生内存碎片。可以定期检查内存使用或在运行数小时后主动重启脚本。功耗与散热持续满负荷运行CPU会导致行空板发热。如果放在封闭空间需要注意散热。对于电池供电的场景需要评估续航。在行空板上成功运行YOLOv8n是一个典型的边缘AI应用落地过程。它不仅仅是敲几行代码更涉及到环境适配、性能权衡、资源管理和系统集成。从最初的“跑不通”到后来的“跑得动”再到最后的“用得上”每一步的坑踩过去你对嵌入式AI开发的理解就会深一层。希望这篇详细的记录能帮你绕过我踩过的那些坑更快地让你的行空板“睁开智慧的眼睛”。