☰
树莓派5部署YOLOv5视觉检测实战:从Ubuntu到车间的六步落地指南
2026/10/1 9:09:28 网站建设 项目流程

把树莓派5塞进车间当视觉检测主机,这个念头在我脑子里转了大半年。真正动手之后,才发现它不光娇气,还很折腾——我好不容易在PC上跑通了自己训练的YOLOv5模型,转头想部署到树莓派5上,却被安装Ubuntu、接摄像头、改推理代码这些看似基础的事连续卡了十几天。整个过程重复踩坑,最后归纳出六件事,每一件都值得单独开一篇。

如果你也打算在产线上用树莓派5做视觉检测、质量判断或者边缘数据采集,这篇就算是给你提前踩一遍雷。我的项目很简单:用一个CSI摄像头对准装配工位,用自己训练的YOLOv5模型识别有没有漏装零件,检测结果通过GPIO联动报警灯。听起来不复杂,但台上一分钟,台下是真的坑坑洼洼。

1. 第一件事:树莓派5安装Ubuntu,开局差点劝退

1.1 为什么我从树莓派OS换成Ubuntu

很多人买回树莓派,第一反应就是刷官方Raspberry Pi OS。但如果目标明确是部署YOLOv5模型,我更推荐直接上Ubuntu。原因有三点:一是Ubuntu Server无桌面版镜像体积小,跑Python推理更清爽;二是Ubuntu对工业环境的软件生态兼容更好,像ROS、Docker、Mosquitto这些常用服务都有现成deb包;三是我个人最在意的,树莓派OS的Python环境偏旧,YOLOv5拉出来的依赖经常要自己编译,而Ubuntu 24.04 LTS自带的Python 3.12,配合pip venv能少踩很多兼容性坑。

当然,树莓派官方OS也不是不能用,只是你在网上搜到的很多树莓派5部署YOLOv5的教程都是基于Ubuntu 22.04或者24.04写的,抄作业更方便。如果你之前用惯了树莓派OS,也不必推翻重来,但请至少用64位版本。现在树莓派5的CPU是4核Cortex-A76,跑64位系统才能把性能吃满。另一个容易被忽略的问题是,Ubuntu Server默认没有桌面环境,对只想做检测服务的人来说是优点,但对需要经常调试的人来说可能不方便。我的做法是先装Server版,后面需要图形界面时再手动装一个轻量桌面,这种“按需加料”的方式比一开始就装Desktop更稳。

1.2 烧录、启动和电源的真实体验

我用的烧录工具是Raspberry Pi Imager,镜像选“Other general-purpose OS”里的Ubuntu Server 24.04 LTS。烧录前记得先点开右下角的齿轮图标,预配置好WiFi、SSH和用户名密码,不然第一次开机没有显示器只能干瞪眼。烧录时间取决于卡的速度,一般一两分钟完成。这里有个经验:如果你同时插了SD卡和NVMe SSD,首次启动会默认从SD卡引导,需要手动修改引导顺序;Ubuntu 24.04的firmware配置比老版本友好,但还是建议烧录前就把配置文件看清楚,省得启动后找不到系统盘。

启动后最容易出问题的不是系统,而是电源。树莓派5的USB-C口支持PD供电,官方标称要5V/5A。我第一次用普通手机充电器,系统能起来,但一接上摄像头和树莓派官方风扇,屏幕就出现电压报警图标,推理程序动不动就崩。后来换了一个带PD协议输出的电源,电压报警直接消失。这里提醒一句,不是电流够就完事,很多充电器对树莓派5的PD握手不兼容,最好用官方电源或者拆机验证过的5V5A电源。如果后面还要挂更多外设,建议直接买工业级导轨电源转5V输出,从根源上解决电压不稳的问题。

存储方面,我强烈建议别用SD卡裸跑。树莓派5支持PCIe接口,配一张NVMe转接板和转接线,就能把系统装在M.2固态上。为什么要这么做?后面“长期运行”那节我会细说,但一句话先撂在这:车间环境里SD卡的损坏率远比你想象的高。加上NVMe之后,启动速度和系统的稳健性是质的提升。另外,树莓派5的原装散热风扇不接的话,夏天跑YOLOv5处理器很容易冲到80摄氏度以上,然后降频到1GHz以下,推理速度肉眼可见地变慢。所以开机第一件事,把散热风扇接上,或者至少装一个够大的铝散热片。电源、存储、散热这三件事,决定后面所有部署能否顺利进行。

2. 第二件事:摄像头与图像采集,车间光源比YOLOv5更磨人

2.1 CSI摄像头选型,接口和排线都有坑

树莓派5上的CSI接口跟以前的旧型号不一样了,是22针的,宽度变得更小。手里如果还留着树莓派4时代用的老款IMX219摄像头模块,普通排线插不进去,必须要买一根22针转15针的转换排线。我一开始没注意这个细节,板子到了之后排线怎么都插不紧,以为是坏了,后来才发现是接口规格换了。

选摄像头方面,如果拍的是静止或者低速移动的工件,普通的Camera Module 3就够了,500万像素,自动对焦版本在调试时能省不少麻烦。如果流水线速度快,需要拍高速运动的物体,那就得选全局快门摄像头,否则拍出来全是运动模糊。树莓派官方有一款6mm广角全局快门摄像头,在车间里很实用,缺点是价格贵。采像原理上,全局快门一次曝光所有像素,和卷帘快门逐行曝光相比,不会出现“果冻效应”,这对测漏装零件这种任务非常关键。

摄像头类型接口适合场景注意点
Camera Module 322针CSI静态或慢速工件自动对焦版在调试时更方便
官方全局快门摄像头22针CSI高速流水线运动工件价格高,但抗运动模糊
USB摄像头USB-A快速验证测试延迟高,CPU占用高,不推荐产线

2.2 libcamera与GStreamer,绕开OpenCV读摄像头的坑

树莓派5跟旧树莓派另一个大的区别是:摄像头底层已经全面切换到libcamera框架,传统raspistill和raspivid命令已经不存在了。Python里如果用OpenCV的cv2.VideoCapture(0)去读官方CSI摄像头,大概率会打开失败,或者读到黑屏。正确做法是让OpenCV走GStreamer管道。

我用的验证命令是:

gst-launch-1.0 libcamerasrc ! video/x-raw,width=1280,height=720,framerate=30/1 ! videoconvert ! autovideosink

如果只是快速验证图像,可直接用libcamera-hello或libcamera-still。但为了后续接入YOLOv5,我建议直接用GStreamer的管道,把CSI摄像头伪装成一个标准v4l2设备。实际使用中,我在Python里这么写:

cap = cv2.VideoCapture( "libcamerasrc ! video/x-raw,width=640,height=480,framerate=15/1 ! " "videoconvert ! video/x-raw,format=BGR ! appsink drop=1" )

注意appsink后面的drop=1,这个参数很关键。当推理速度跟不上采集速度时,OpenCV会自然丢弃旧帧,保证读到的永远是最新一帧,避免队列越积越多导致延迟越来越大。如果你只是做单张图片检测,也可以直接走libcamera-still --output,但要额外处理子进程调用,不如GStreamer管道灵活。

2.3 车间光照和曝光参数调整实战

车间环境最难受的是光源不固定:白天有窗户光、晚上有高频灯,工件表面还会有反光。YOLOv5模型训练时拍的照片可能是均匀光照,一到现场就各种过曝欠曝,检测率直线下降。我的处理办法是先用libcamera-still连续拍几十张,把曝光时间和增益范围确定下来。如果现场光线基本固定,可以固定曝光参数,不让自动曝光来回跳:

libcamera-hello --set exposure_time 20000 --set gain 1.5

在Python代码里,也可以借用libcamera的controls设置曝光。这里要特别提醒,自动曝光在视觉算法里常常是敌人,因为检测算法期望输入图像的统计分布稳定。固定曝光、固定白平衡,甚至固定对焦距离,能显著减少误检。我后来还在镜头前加了一块偏振片,专门压反光,效果立竿见影——车间金属工件表面的高光区不再把检测框带偏。

3. 第三件事:自训练的YOLOv5模型部署,最大的坎在模型转换

3.1 先在PC上把模型导出成ONNX

训练YOLOv5的过程就不展开说了,网上教程一抓一大把。重点是从best.pt到能跑在树莓派5上的推理格式。我这里选择的是ONNX,原因很简单:ONNX Runtime在ARM64上有官方wheel包,而且针对CPU做了线程级优化;PyTorch直接在树莓派上跑,不但包体积大,推理速度也慢。

导出命令是在PC上完成的,YOLOv5官方仓库自带export.py:

python export.py --weights best.pt --include onnx --simplify --opset 12

如果你的模型类别数不是默认的80,也不用担心,导出时会自动带上训练时的nc参数。导出完成后会得到一个best.onnx,可以用Netron打开检查计算图,确认最后一个输出层的维度是不是[1, 25200, 5+nc]。25200来自640分辨率下三个尺度特征图的总和:80×80、40×40、20×20,每个网格还有三个anchor,所以8400×3=25200。如果你导出时指定了更小的imgsz,比如320,那么三个特征图是40×40、20×20、10×10,输出就会变成6300个候选框,推理压力小很多。

3.2 树莓派5上的Python推理环境搭建

在树莓派5上创建一个干净的虚拟环境,这是吃了太多次系统Python环境被搞烂的教训。直接在系统全局pip install,很容易跟Ubuntu自带的包冲突,而且后面换模型、换依赖版本时会很痛苦。我习惯把项目隔离在venv里:

python3 -m venv ~/venv/yolov5 source ~/venv/yolov5/bin/activate pip install --upgrade pip pip install onnxruntime opencv-python-headless numpy

opencv-python-headless比opencv-python更适合服务器场景,少带GUI依赖。如果你的Ubuntu还没装系统基础包,可能还要装libglib2.0-0。onnxruntime建议装最新版,至少是1.16以上,旧版本在Python 3.12上可能没有对应wheel。装好之后,可以先用一行命令验证ONNX Runtime能不能正常加载模型:

python -c "import onnxruntime as ort; s=ort.InferenceSession('best.onnx',providers=['CPUExecutionProvider']); print(s.get_inputs()[0].shape)"

如果输出的是[N,3,640,640]之类的维度,环境就通了。如果报错,最常见的原因是onnxruntime装成了x86版本,或者在树莓派5的ARM64系统上pip拉到了一个老的wheel。这时候强制卸掉重装最新版,基本能解决。

3.3 ONNX推理与后处理代码,照着抄就行

官方YOLOv5仓库里的detect.py也能在树莓派上跑,但那些代码里包含很多跟推理无关的配置,速度也不够极致。我这里把最核心的推理逻辑提炼出来:

import cv2 import numpy as np import onnxruntime as ort class Detector: def __init__(self, onnx_path, conf_thres=0.25, iou_thres=0.45): self.session = ort.InferenceSession(onnx_path, providers=['CPUExecutionProvider']) self.conf_thres = conf_thres self.iou_thres = iou_thres self.input_name = self.session.get_inputs()[0].name self.input_shape = self.session.get_inputs()[0].shape self.out_names = [o.name for o in self.session.get_outputs()] def letterbox(self, img, new_size=(640, 640)): h, w = img.shape[:2] r = min(new_size[0] / h, new_size[1] / w) nw, nh = int(round(w * r)), int(round(h * r)) img_resized = cv2.resize(img, (nw, nh), interpolation=cv2.INTER_LINEAR) canvas = np.full((new_size[0], new_size[1], 3), 114, dtype=np.uint8) top = (new_size[0] - nh) // 2 left = (new_size[1] - nw) // 2 canvas[top:top+nh, left:left+nw] = img_resized return canvas, r, left, top def detect(self, img): canvas, r, left, top = self.letterbox(img, (640, 640)) blob = canvas[:, :, ::-1].transpose(2, 0, 1)[None].astype(np.float32) / 255.0 out = self.session.run(self.out_names, {self.input_name: blob})[0][0] xc = out[:, 4] > self.conf_thres boxes = out[xc] results = [] if len(boxes) == 0: return [] for *xyxy, conf, cls in boxes: if conf < self.conf_thres: continue x1, y1, x2, y2 = (xyxy - np.array([left, top, left, top])) / r results.append([x1, y1, x2, y2, conf, cls]) if not results: return [] results = np.array(results) indices = cv2.dnn.NMSBoxes(results[:, :4].tolist(), results[:, 4].tolist(), score_threshold=self.conf_thres, nms_threshold=self.iou_thres) if len(indices) == 0: return [] return results[np.array(indices).flatten()]

实际使用的时候,还需要注意输入图像最好是BGR顺序,letterbox后的图像要转成RGB、归一化到0到1、变成NCHW格式,再放进session.run。这一步是新手最容易漏的地方,漏了之后推理结果全部乱套。我自己第一次跑的时候,忘了除以255,出来的检测框置信度全部异常,排查了大半天。

后处理里的NMS,我更推荐用cv2.dnn.NMSBoxes,因为OpenCV里的实现是C++写的,比纯Python循环快很多。如果类别数量多,建议把NMS逻辑改成numpy并行版本。不过对咱们这种小项目来说,cv2版已经足够。

4. 第四件事:算力不够,怎么把YOLOv5的速度抠出来

4.1 模型尺寸与分辨率,先砍一刀

树莓派5没有独立NPU,所有计算都靠4颗A76核心。因此,最直接的优化就是先把输入分辨率从640降到416或者320。检测目标如果只是“有没有漏装零件”,320分辨率足够了;降分辨率带来的是计算量平方级下降,FPS提升非常明显。我在测试中把640降到320后,推理时间大约能缩短一半还多。当然,分辨率不是越低越好,降太狠小零件会直接糊掉,需要拿你的实际数据集做对比。

模型本身也别贪大。YOLOv5s是性价比最优选,YOLOv5m和YOLOv5l在树莓派上跑起来非常痛苦。我自己试过YOLOv5s和YOLOv5n,工件特征比较小的情况下yolov5n会漏检,s基本够用。另外,可以尝试把模型量化成FP16,ONNX Runtime在CPU上对FP16不一定比FP32快,但部分情况下能减少内存占用,这个需要实测,不要盲目相信教程。剪枝蒸馏那种高阶玩法,对小项目来说投入产出比不高,除非你整个团队都有做算法优化的经验。

4.2 推理管线和线程优化

很多人在树莓派上写代码,还是PC时代的思维:单线程循环里一边读摄像头一边做推理。在树莓派上这么干,读帧和推理互相抢CPU,帧率很难看。正确做法是拆成两个进程或至少两个线程:主线程负责取帧,把最新一帧丢进队列;推理线程永远只处理队列里最新的帧,如果推理跟不上,宁可丢帧也不阻塞采集。

把采集的GStreamer管道写成:

cap = cv2.VideoCapture("libcamerasrc ! video/x-raw,width=640,height=480,framerate=15/1 ! videoconvert ! video/x-raw,format=BGR ! appsink drop=1")

然后用queue.Queue(maxsize=1)来保证最新帧。之所以maxsize设为1,是为了避免积压几十帧后推理滞后,实时性反而更差。如果系统还有多余CPU,建议用multiprocessing替代多线程。Python的GIL对onnxruntime影响比较小,但OpenCV的读帧和预处理受GIL影响明显,多进程可以同时跑采集、预处理、推理三件事。

4.3 树莓派5的定位:不是每个检测都要24帧

我也踩过“一定要跑到30FPS”的执念。但回到车间场景,检测漏装零件通常只需要几秒一拍,检测完再配合复检或停机,根本不需要连续视频流。所以最终我把推理输入分辨率定为416,单帧推理时间稳定在1秒以内,整个系统足够用。启停时间和告警联动都基于这个节奏来配,反而比追求高帧率更稳。

如果你确实需要高帧率,树莓派5的CPU就力不从心了。我建议把树莓派5用在低功耗、低成本、算力需求不高的边侧场景,需要高帧率连续检测时,直接上带NPU或者GPU的边缘设备更省心。树莓派5的价值在于功耗低、体积小、方便改造旧设备,而不是跟专业工控机拼算力。搞明白定位之后,你才不会陷入无休止的调参焦虑。

5. 第五件事:车间长期运行,供电、散热和死机恢复缺一不可

5.1 电源和散热,别让设备死在夏天

车间环境最现实的问题是电压不稳。普通家用电源在车间里会受到大型设备启停影响,瞬间跌落会让树莓派重启。所以建议不要直接插墙插,而是用一个工业级开关电源转5V,或者至少一个带过压保护的适配器。我买过一个号称5V5A的杂牌电源,带载能力不足,树莓派5高负载运行半小时后就开始缓存报警,后来换成有3C认证的电源才稳定。

温度方面,树莓派5的高性能核心发热量不小。如果机器放在不通风的控制柜里,夏天环境温度35度时,树莓派核心能冲到85度。温度一高,系统会自动降频,推理速度变得极其不稳定。我的方案是:主动散热风扇加散热片,机柜侧板开孔装一个温控风扇;树莓派侧设置一个systemd定时任务,超过70度就把风扇开到全速。定期清理风扇灰尘也很关键,车间粉尘多,两个月不清风扇就变成积灰陀螺,散热效率大打折扣。

5.2 开机自启、看门狗和掉电后的惨痛教训

检测程序如果还要人工去开机点一下,那就不叫进车间。我写了一个systemd服务,开机自动启动Python脚本,并加了Restart=always,让进程在崩溃或异常退出后自动拉起来。服务文件大致如下:

[Unit] Description=YOLOv5 Detection Service After=network.target [Service] User=pi WorkingDirectory=/home/pi/detect ExecStart=/home/pi/venv/yolov5/bin/python /home/pi/detect/run.py Restart=always RestartSec=3 [Install] WantedBy=multi-user.target

这个服务的价值在于崩溃自动恢复,但如果整个系统死机、硬件看门狗触发不了,程序还是可能停摆。所以我再加了系统层watchdog,在/boot/firmware/config.txt里启用bcm2712 watchdog,然后配置systemd的RuntimeWatchdogSec和ShutdownWatchdogSec,保证树莓派5死机后能自动断电重启。这里注意不同内核配置项名称,树莓派5上最常见的是bcm2712_wdt。掉电这个问题,我再三强调别用SD卡裸跑。我有一次车间意外断电,再上电后SD卡文件系统损坏,连ext4都挂载不了。后来换成NVMe SSD,加上开机自动fsck,断电后再启动基本没有丢过系统。

5.3 存储保护:SD卡不是车间的“存尸柜”

只要条件允许,系统盘一定放在NVMe SSD上。树莓派5的PCIe 2.0带宽虽然不算高,但跑一个视觉服务绰绰有余。SSD没有SD卡那种猝死式损坏,连续读写寿命也高得多。开销大概就是一块M.2转接板加一块二手固态的钱,对比产线停机的损失,完全可以接受。

如果实在要用SD卡,还有个办法是开启overlayfs,把系统分区设为只读,日志和检测结果写到内存盘或者U盘。这样SD卡基本不会在读面上出问题,但代价是每次配置重启后不保留,适合那种“配置好后不再动”的稳定场景。我经历过一次SD卡“读卡器写坏”之后,对这种做法特别推荐。另外无论哪种存储,建议都要定期备份模型和关键配置文件,哪怕只是打包丢到另一台机器上,也比出问题后重配全部环境省事得多。

6. 第六件事:让树莓派5真正进产线,不能只跑一个算法脚本

6.1 外围设备与GPIO/串口对接

视觉检测最终要跟产线交互,不能只在屏幕上画框。最简单的联动方式是GPIO:检测到缺陷时,拉高一个GPIO引脚,触发继电器给报警灯供电,或者给PLC一个24V信号。这里要注意,树莓派GPIO是3.3V电平,不能直接接24V系统,一定要经过光耦或者继电器模块隔离。我一开始直接用杜邦线接继电器模块,结果现场有电磁干扰,误触发了几次,后来换成光耦隔离模块才好。

如果设备支持Modbus RTU,可以用USB转485模块,通过pyserial按照Modbus协议读写寄存器。我从PLC手册里摸了一下午,最后用modbus-tk把检测结果写入保持寄存器。这里要提前规划好寄存器地址和字节序,别等到了现场再翻手册。车间里还有种常见需求是声光报警,用继电器控制即可。需要接多个输出时,建议用I2C扩展GPIO,或者用串口控制小型继电器板,别一根根杜邦线乱接。

6.2 检测数据上报与产线看板

检测结果不能只存在本地SD卡里。我用了一个非常简单的方式:脚本每完成一次检测,就把带框图片保存到本地目录,同时把统计结果通过MQTT发布到产线看板。MQTT的好处是,即使看板偶尔断线,消息也能通过broker缓存,重新上线后补发。

上报代码逻辑大约是这样:

import paho.mqtt.client as mqtt client = mqtt.Client() client.connect('192.168.1.50', 1883) client.publish('line1/vision/result', f'{ok_count} {ng_count}')

如果厂里已有MES系统,也可以把结果从MQTT转发到数据库。关键是把检测结果结构化,是OK还是NG、缺陷类型、置信度、时间戳,这些字段一定要提前定义好,不然报表写一半要改结构,很痛苦。我这里把图片命名规则设成了“日期_时间_结果_置信度.jpg”,后面追溯异常工件时非常方便。日志也不能只存在树莓派本地,最好定时同步到文件服务器,至少保留三个月,方便质量部门复盘。

6.3 运维视角:模型更新与远程管理

模型上线后发现误判率高,肯定要迭代。树莓派5上不能每次重新训练都跑过去插电脑,我把模型放在固定目录,训练完通过scp上传,再用软链接切换版本。脚本里每次启动时读取模型文件,这样只要更新软链接并重启服务,就能完成模型升级。我还会在服务里加一个模型时间戳检查,如果模型文件发生变化就自动重启推理流程,省去手动操作。

远程管理方面,我采取的策略很简单:在树莓派上配置固定IP,厂内局域网随时可以SSH登录,需要跨网段访问时通过已有IT规划的跳板机走正规通道。直接用远程桌面做图形界面调试也行,但生产环境我尽量不开图形服务,省内存也少暴露攻击面。每次远程改完代码,先用日志确认没过热、没爆内存,再离开。这习惯帮我避开了好几次“远程改完第二天才发现服务没起来”的尴尬。

六件事走下来,树莓派5已经从桌面的玩具变成了车间角落里一个不太起眼的铁盒子。我个人最大的体会是,它的算力不算强,但足够解决很多实际检测问题,尤其是在成本敏感、空间有限的老设备改造中,优势很明显。如果你想把它搬进产线,别急着追求模型效果,先把系统、摄像头、模型转换、稳定运行这六件事踏踏实实做好,八成也能顺利落地。最后再多说一句,所有的坑都不会白踩,下一次再看到树莓派,你会先看一眼电源和散热。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询