一:前言

我在尝试做基于树莓派的机器视觉过程中遇到了不少问题,其中大部分在网上资料以及ai的帮助下得以解决,但正如我题目中所说的,网上在这方面的资料较少,因此本文将聚焦于这两个问题,阐释可能导致它们的原因并给出至少适用于我的树莓派平台的解决办法

二:硬件介绍

树莓派4b(4GB内存,系统为官方64位Raspberrypi OS)*1,5V3A电源*1,网线*1,USB摄像头*1

电脑(Win11系统)*1

三:软件介绍

我用到了这三个软件:

Raspberry Pi Imager:树莓派官方烧录程序:下载链接

MobaXterm:用于ssh远程连接树莓派:下载链接

vnc viewer:用于vnc远程连接树莓派桌面:下载链接(有点慢)

四:问题介绍与解决

1:SSH模式无法联网

由于我并没有准备键盘和小屏幕,按照网上资料所说的ssh远程连接,其中卡到了等待树莓派联网这一步

按照网上绝大多数教程所说,向烧录好系统的存储卡内加入了ssh与wpa_supplicant.conf并配置好网络信息后插入树莓派并通电,等待相当长时间后路由器上并没有找到其对应的ip,查找资料后发现有人称这种远程连接不是很稳定,但像我这么极端压根连不上的却罕有,为了解决这个问题,我用一根网线将其连接到电脑上,利用cmd找出其对应ip,操作如下:

1:准备一根网线(我这个是从路由器上拆下来的):

2:将其连接树莓派和你电脑的网口

3:Win+R,输入ncpa.cpl打开网络连接:

4:右键WLN->属性->共享->勾选“允许其他用户通过此计算机的Internet连接来连接”,在下方的选择窗里勾选上你要给树莓派连接的有线网(我这里是以太网、以太网2,有些电脑上可能就是WLAN1、WLAN2……,判断哪个是树莓派的方法是插上网线并通电以后对应的十叉会消失,虽然显示未识别的网络但其实已经连接上了)

5:右键对应的网络(比如我的“以太网”),取消勾选IPv6,勾选IPv4,不然ip地址可能是动态的

6:确认连接成功后打开cmd,输入arp -a,在192.168.137.1下寻找可疑ip(可以将网线拔掉后重新插上,找多出来的ip),一般来说,一张sd卡只对应一个固定ip,哪怕你将多个系统卡分别插在一张树莓派4b上其对应的ip也是唯一的,我的树莓派对应.235那个

7:后续步骤就跟网上教程一样,这里就不多赘述了,将其通过ssh远程连接,之后sudo raspi-config,经过一系列设置后开启VNC功能,再用VCN Viewer连接远程桌面

不过这种操作会使电脑网速变慢,用完了记得关回去就是了

2:Ultralytics包Illegal Instruction问题

这个问题着实困扰了我好久,其表现为在使用清华源配置好Ultralytics包之后使用形如“Yolo detect predict model=yolov8n.pt”之类的命令试图获取Yolo源码时报错:Illegal Instruction(非法指令),此外,在命令行直接输入python3 -c指令时也时不时报这个错误,上网查阅后发现不止我的4b,一些3b板子也会遇到这个问题,可是5b在执行该命令时就可以执行

经过查证,这个问题源于清华源中的torch为x86架构,而树莓派则是arm64架构,其指令集不同,因此在pc端可以运行的指令在树莓派上就会报错。另外,ai跟我说树莓派上的Python13版本过高可能也是一个原因。总之,我曾尝试删掉torch,安装符合arm64架构的版本失败;安装Python11的尝试也失败

后续,我换了NCNN,来试图绕过torch,最终成功了,在树莓派上运行了yolov8模型:

1:在pc端安装

pip install torch torchvision onnx onnxsim
git clone https://github.com/Tencent/ncnn

2:转化训练好的.pt模型

# export_yolov8_to_ncnn.py
from ultralytics import YOLO

# 加载模型并导出 ONNX,注意下面的.pt路径换成你自己的模型路径
model = YOLO('yolov8n.pt')
model.export(format='onnx', imgsz=640)  # 生成 yolov8n.onnx

3:以NCNN转化

# 在 PC 上(需编译 ncnn/tools)
onnxsim yolov8n.onnx yolov8n-sim.onnx
./ncnn/build/tools/onnx/onnx2ncnn yolov8n-sim.onnx yolov8n.param yolov8n.bin

如果是第一次转化的话,有时候会提示缺包并卡死,这时候Ctrl+C退出,自己配好缺的包再次运行,之后还会卡一段时间,问是否同意发送,输入yes即可

pip install onnx onnxslim -i https://pypi.tuna.tsinghua.edu.cn/simple

4:在树莓派端配置好onnx runtime包

pip install onnxruntime numpy opencv-python pillow -i https://pypi.tuna.tsinghua.edu.cn/simple

5:在pc端利用scp指令将转换好的.onnx文件发送到树莓派,注意替换相关路径与ip

scp F:\Anaconda_envs\envs\yolov8\yolov8-main\runs\train\exp6\weights\best-sim.onnx raspberrypi@192.168.137.235:~/yolo/

6:发送写好的推理脚本,理论上这一步你完全可以在树莓派端通过nano完成,但我懒得敲(~ ̄▽ ̄)~

import cv2
import numpy as np
import onnxruntime as ort
import time

# ==============================
# 配置区(按需修改)
# ==============================
MODEL_PATH = "best.onnx"          # 模型路径
INPUT_SIZE = (640, 640)           # 输入尺寸(必须和训练一致)
CONF_THRESHOLD = 0.5              # 置信度阈值
IOU_THRESHOLD = 0.45              # NMS 阈值
CAMERA_INDEX = 0                  # 0=CSI/USB摄像头, -1=自动尝试

# 如果你知道类别名(比如 ['person', 'car', ...]),填在这里
CLASS_NAMES = ["Corrosion","Cracks","Scale","blockage"]  # 替换为你的真实类别!

# ==============================
# 加载 ONNX 模型
# ==============================
print("Loading model...")
session = ort.InferenceSession(MODEL_PATH, providers=["CPUExecutionProvider"])
input_name = session.get_inputs()[0].name
print("Model loaded!")

# ==============================
# 初始化摄像头
# ==============================
print("Opening camera...")
cap = cv2.VideoCapture(CAMERA_INDEX)

# 尝试设置分辨率(降低可提升 FPS)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

if not cap.isOpened():
    print("Error: Cannot open camera!")
    exit()

# ==============================
# 辅助函数
# ==============================
def xywh2xyxy(x):
    y = np.copy(x)
    y[..., 0] = x[..., 0] - x[..., 2] / 2
    y[..., 1] = x[..., 1] - x[..., 3] / 2
    y[..., 2] = x[..., 0] + x[..., 2] / 2
    y[..., 3] = x[..., 1] + x[..., 3] / 2
    return y

def process_frame(frame):
    h, w = frame.shape[:2]
    scale = min(INPUT_SIZE[0] / h, INPUT_SIZE[1] / w)
    new_h, new_w = int(h * scale), int(w * scale)
    
    resized = cv2.resize(frame, (new_w, new_h))
    padded = np.full((INPUT_SIZE[0], INPUT_SIZE[1], 3), 114, dtype=np.uint8)
    padded[:new_h, :new_w] = resized
    
    blob = padded.transpose(2, 0, 1)[np.newaxis, ...].astype(np.float32) / 255.0
    return blob, scale, w, h

# ==============================
# 主循环
# ==============================
print("Starting inference... Press 'q' to quit.")
while True:
    ret, frame = cap.read()
    if not ret:
        print("Failed to grab frame")
        break

    start_time = time.time()
    
    # 预处理
    blob, scale, orig_w, orig_h = process_frame(frame)
    
    # 推理
    outputs = session.run(None, {input_name: blob})[0]
    
    # 后处理
    pred = np.squeeze(outputs).T
    boxes = pred[:, :4]
    scores = np.max(pred[:, 4:], axis=1)
    class_ids = np.argmax(pred[:, 4:], axis=1)
    
    # 过滤
    mask = scores > CONF_THRESHOLD
    boxes, scores, class_ids = boxes[mask], scores[mask], class_ids[mask]
    
    if len(boxes) > 0:
        # xywh -> xyxy
        boxes = xywh2xyxy(boxes)
        # 缩放回原图
        boxes[:, [0, 2]] /= INPUT_SIZE[1]
        boxes[:, [1, 3]] /= INPUT_SIZE[0]
        boxes[:, [0, 2]] *= orig_w
        boxes[:, [1, 3]] *= orig_h
        
        # NMS
        indices = cv2.dnn.NMSBoxes(
            boxes.tolist(), scores.tolist(), CONF_THRESHOLD, IOU_THRESHOLD
        )
        
        # 绘图
        if len(indices) > 0:
            for i in indices.flatten():
                x1, y1, x2, y2 = map(int, boxes[i])
                label = f"{CLASS_NAMES[class_ids[i]]} {scores[i]:.2f}" if class_ids[i] < len(CLASS_NAMES) else f"Class{class_ids[i]} {scores[i]:.2f}"
                cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
                cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
    
    # 显示 FPS
    fps = 1.0 / (time.time() - start_time)
    cv2.putText(frame, f"FPS: {fps:.1f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    
    # 显示画面
    cv2.imshow("YOLOv8 ONNX - Press 'q' to quit", frame)
    
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ==============================
# 清理
# ==============================
cap.release()
cv2.destroyAllWindows()
print("Done.")

7:树莓派端确认收到后运行

python 2rspi.py

成功在树莓派上部署yolov8,效果如下

大功告成

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐