公司动态
RDK X5加速yolov5(BPU零基础入门)
目录一、安装 Docker码头工人二、训练自己的 YOLOv5 模型1. 准备工作1.1 下载 YOLOv5 源代码1.2 下载预训练模型2. 下载 Anaconda 并配置环境2.1 下载 Anaconda2.2 配置环境2.3 安装 PyTorch2.4 安装其他软件包2.5 安装 labelimg2.6 测试环境配置3. 训练模型3.1 标注图片3.2 检查标签3.3 更改运行文件3.4 修改 train.py3.5 运行训练3.6 模型文件说明三、模型转换 PT 转 ONNX四、模型使用4.1 调用摄像头五、准备预处理的文件5.1 codes 文件夹5.2 data 文件夹5.3 Convert_x5.yaml 文件5.4 batch_calib_bin.py 文件六、Docker 拉取 RKD X5 镜像6.1 有 NVIDIA 显卡6.2 无显卡 CPU6.3 解压 OE 工具包6.4 下载工具包七、模型转换与部署7.1 启动 Docker 容器7.2 验证环境7.3 转换图像7.4 检查 ONNX 模型7.5 模型转换7.6 上板检测八、推理代码一、安装docker码头工人去docker官方网站 下载安装包找到对应的Windows下载最新版安装包下载完成后双击进行安装二、训练自己的yolov5模型会的可以直接跳过看 - 三、拉取镜像及推理加速1.准备工作下载yolov5源代码下载地址GitHub - ultralytics/yolov5: YOLOv5 in PyTorch ONNX CoreML TFLite进入网站之后按路径master-Tags找到不同版本选择自己需要的版本我这里选择的是6.2版本单击V6.2我们再按路径点击Code - Download ZIP 即可下载代码下载预训练模型还是刚刚的界面我们往下划找到侧边的Releases单击进入之后我们按路径点击 Tags - V6.2进入之后鼠标一直向下滑动找到Assets 然后找到 yolov5s.pt单击下载也可下载yolov5l.ptyolov5m.pt根据需求来选择。代码和预训练模型下载好之后把代码解压把下载好的预训练模型放置在下载的源码中2.下载Anaconda并配置环境下载AnacondaAnaconda下载网站conda下载网站我们进入上述网站找到Windows对应的安装包点击Download安装包下载好之后我们双击在这里我们有几个事项要注意扩展熟悉命令窗口简单指令conda简单指令 conda --version #查看conda版本验证是否安装 conda env list #查看所有环境 conda create -n env_name package_name #创建名为env_name的新环境并在该环境下安装名为package_name 的包可以指定新环境的版本号 例如conda create -n python2 pythonpython2.7 numpy pandas创建了python2环境python版本为2.7同时还安装了numpy pandas包 conda activate env_name #进入env_name环境 conda deactivate #退出环境 conda create --name new_env_name --clone old_env_name #复制old_env_name为new_env_name conda remove --name env_name –all #删除环境 conda list #查看所有已经安装的包 conda install package_name #在当前环境中安装包 conda install --name env_name package_name #在指定环境中安装包 conda remove – name env_name package #删除指定环境中的包 conda remove package #删除当前环境中的包配置环境1····1在win开始目录里找到Anaconda3文件打开Anaconda prompt输入 :conda create -n yolo python3.8.5 #创建一个名字叫yolo的环境使用python3.8.5#下图环境的名称是yolo5这是因为我已经有yolo的环境了所以用yolo5的名称来做演示输入yes耐心等待下载安装完成半小时左右下载好之后我们输入 conda activate yolo可以看到环境从base变成yolo了除了环境空间我们还需要安装软件包打开pytorch官网Pytorch官网我们按照顺序有GPU的电脑Windows - Conda - Python - CUDA只有CPU的电脑Windows - Conda - Python - CPU复制出现的命令行再次打开Anaconda Prompt然后先切换为yolo环境再粘贴命令行输入yes安装完成后进入py环境输入指令验证后面的查看cuda是否可用是指是否可以调用gpu只有为ture才能调用gpu其他软件包的安装我们进入yolo环境然后移动到yolov5源代码的位置 再用dir指令显示文件夹的文件。可以看到有个叫requirements.txt的文件里面放的是yolov5要安装的软件包和版本使用pip下载requirements.txt里所有软件包pip install -r requirements.txt如果下载过慢甚至报错退出可以使用国内镜像pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/安装labelmepip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple运行我们用VSCode打开yolov5测试环境配置是否正确打开VScode找到yolov5文件目录选择detect.py文件在右下角选择编译器为yolo我们运行detect.py代码等待运行结束我们可以发现多出了一个runs/detect/exp路径这样的话即代表环境已经配置好了接下来我们进行下一步训练模型3.训练模型1标注图片首先对着要识别的物体拍一大堆照片然后按照下图顺序新建文件夹mydata├─ images│ ├─ test # 下面放测试集图片│ ├─ train # 下面放训练集图片│ └─ val # 下面放验证集图片└─ labels├─ test # 下面放测试集标签├─ train # 下面放训练集标签├─ val # 下面放验证集标签如图mydata 文件夹的位置可以随意放置记住路径即可但mydata的内部文件夹的名称与位置要与上面图片一致。注意图片数量至少50张起步越多越好没有上限。注意如果文件名字太乱可以用下面的代码进行更改不过不改也没有太大问题import os.path def rename(img_folder,num): for img_name in os.listdir(img_folder): # os.listdir() 列出路径下所有的文件 #os.path.join() 拼接文件路径 src os.path.join(img_folder, img_name) #src要修改的目录名 dst os.path.join(img_folder, D str(num) .jpg) #dst 修改后的目录名 注意此处str(num)将num转化为字符串,继而拼接 num num1 os.rename(src, dst) #用dst替代src def main(): img_folder0 F:/工训/Data/Test1 # 图片的文件夹路径 直接放你的文件夹路径即可 num1 rename(img_folder0,num) if name main: main()回到Anaconda prompt 进入yolo环境输入labelimg可以看到弹出了一个界面在view中设置auto save mode自动保存扩展labelimg的快捷键实际大部分时候只用w键创建标注框使用d、a键切换上下图片用w框选要识别的物体输入标注物体名字标签我标注的是其他垃圾Others然后按d键切换下一张直到标注完所有图片的注意可以一次标注多个不同物体这里演示所以只识别一种物体2检查标签打开mydata\labels\train文件可以看到有很多txt文件,点开第一个classes.txt文件如果你标注了几种这里就会有几种标注完成后打开mydata/images/train文件夹将train里的图片挑选出大概10张总图片的10%特别清晰明显的图片复制到images/val和images/test中。val验证集用于调整训练曲线比如当你的模型训练是遇到了模糊的图片或者其他玄学原因导致模型出现了偏差但是模型隔一段时间就会去跑一次验证集这个时候验证集清晰的图片就会把这个偏差给去除防止我们最后得到的模型精度偏差太大。test测试集是最后模型训练完成了测试一下模型的精度。图片复制完后就要复制图片的标注文件打开mydata/labels/train文件夹复制你刚才挑选的图片的对应标注文件复制到/labels/val与/labels/test注意实际这两个集可以直接为空或者直接将训练集里复制所有文件都复制到测试集和验证集里我这里是直接复制训练集里所有文件到另外两个集里3更改运行文件将运行文件中的数据位置改成我们文件实际位置打开yolov5\data 文件夹找到coco128.yaml文件复制一份重命名为mydata.yaml打开mydata.yaml修改注释path行修改为trainmydata/images/train此项为训练集相对相对位置相对于主程序train.py的位置修改val: mydata/images/val此项为验证集的相对位置修改test: mydata/images/test此项为测试集的相对位置修改nc4 此项为标签种类数我只标记了一种所以此处为1修改names: [ Others, Recycle, Harmful, Kitchen]更改你的标签名称有几种写几种注释download 此项为下载yolov5的默认脚本对我们没用修改yolov5s.yaml打开yolov5\models找到yolov5s.yaml复制一份重命名为myyolov5s.yaml打开myyolov5s.yaml只需要修改一项将nc改为4此项为我们的标签种类数注意我们发现除了yolov5s.yaml还有yolov5l.yaml、yolov5m.yaml、yolov5n.yaml、yolov5x.yaml打开这些文件仔细观察发现只有这两项不一样这两项是指神经网络的深度和宽度在使用过程中这两项的数值越大模型的精度越高但所需要的时间也越多模型精度从小到大依次为yolov5n.yaml、yolov5s.yaml、yolov5m.yaml、yolov5l.yaml、yolov5x.yaml如果你的显卡配置够好的话可以选择yolov5x.yaml如果差一点的话可以选择yolov5n.yaml我这里选择yolov5s.yaml4修改train.py打开yolov5文件夹找到 train.py文件复制重命名为mytrain.py打开mytrain.py下滑到大概434行左右找到def parse_opt(knownFalse)我们只需要修改这一段代码中的少数几行中default字样之后的文字parser argparse.ArgumentParser() parser.add_argument(--weights, typestr, default yolov5s.pt, helpinitial weights path) #预训练模型可不填但效果会很差默认为yolov5s.pt parser.add_argument(--cfg, typestr, defaultmodels/myyolov5s.yaml, helpmodel.yaml path) #必要修改 #修改地址为models/myyolov5s.yaml训练模型文件位置 parser.add_argument(--data, typestr, defaultdata/mydata.yaml, helpdataset.yaml path) #必要修改 #修改地址为data/mydata.yaml数据集文件位置 parser.add_argument(--hyp, typestr, default data/hyps/hyp.scratch-low.yaml, helphyperparameters path) parser.add_argument(--epochs, typeint, default100) #修改训练轮数为100轮数越高精度越高速度越慢默认300 parser.add_argument(--batch-size, typeint, default16, helptotal batch size for all GPUs, -1 for autobatch) #线程数默认为16越大越快不能过大 parser.add_argument(--imgsz, --img, --img-size, typeint, default640, helptrain, val image size (pixels)) #图片大小越小越快但精度越差一般图片多大填多大不规则填最长边尺寸 parser.add_argument(--rect, actionstore_true, helprectangular training) parser.add_argument(--resume, nargs?, constTrue, defaultFalse, helpresume most recent training) #继续训练如果上一次训练中止了只要在这里填上Ture就可以继续完成上一次训练 parser.add_argument(--nosave, actionstore_true, helponly save final checkpoint) parser.add_argument(--noval, actionstore_true, helponly validate final epoch) parser.add_argument(--noautoanchor, actionstore_true, helpdisable AutoAnchor) parser.add_argument(--noplots, actionstore_true, helpsave no plot files) parser.add_argument(--evolve, typeint, nargs?, const300, helpevolve hyperparameters for x generations) parser.add_argument(--bucket, typestr, default, helpgsutil bucket) parser.add_argument(--cache, typestr, nargs?, constram, help--cache images in ram (default) or disk) parser.add_argument(--image-weights, actionstore_true, helpuse weighted image selection for training) parser.add_argument(--device, default0, helpcuda device, i.e. 0 or 0,1,2,3 or cpu) #必要修改 #修改为00的意思是调用gpu0如果你的电脑有几块gpu可以填123如果没有gpu可以在上面填cpu parser.add_argument(--multi-scale, actionstore_true, helpvary img-size /- 50%%) parser.add_argument(--single-cls, actionstore_true, helptrain multi-class data as single-class) parser.add_argument(--optimizer, typestr, choices[SGD, Adam, AdamW], defaultSGD, helpoptimizer) parser.add_argument(--sync-bn, actionstore_true, helpuse SyncBatchNorm, only available in DDP mode) parser.add_argument(--workers, typeint, default8, helpmax dataloader workers (per RANK in DDP mode)) parser.add_argument(--project, defaultROOT / runs/train, helpsave to project/name) parser.add_argument(--name, defaultexp, helpsave to project/name) parser.add_argument(--exist-ok, actionstore_true, helpexisting project/name ok, do not increment) parser.add_argument(--quad, actionstore_true, helpquad dataloader) parser.add_argument(--cos-lr, actionstore_true, helpcosine LR scheduler) parser.add_argument(--label-smoothing, typefloat, default0.0, helpLabel smoothing epsilon) parser.add_argument(--patience, typeint, default100, helpEarlyStopping patience (epochs without improvement)) parser.add_argument(--freeze, nargs, typeint, default[0], helpFreeze layers: backbone10, first30 1 2) parser.add_argument(--save-period, typeint, default-1, helpSave checkpoint every x epochs (disabled if 1)) parser.add_argument(--seed, typeint, default0, helpGlobal training seed) parser.add_argument(--local_rank, typeint, default-1, helpAutomatic DDP Multi-GPU argument, do not modify)注意必须要修改 --data --cfg --batch-size 这三行代码其他的可以稍微调整来提高精度或减少训练时间改完之后运行mytrain.py代码等待程序运行完成打开yolov5\runs\train找到最新的exp前缀的文件夹我这里是exp8点击进入后打开weights文件夹可以看到有best.pt和last.pt这两个文件就是我们训练得到的模型。best.pt 和 last.pt 是在训练深度学习模型时保存的两个不同的模型文件。 last.pt 包含最后一次训练迭代的模型参数。在训练期间每次完成一次迭代时模型参数都会被保存在 last.pt 文件中。因此当您使用 last.pt 文件时您将得到最后一次训练迭代的模型。 best.pt 包含在整个训练过程中获得最佳性能的模型参数。通常这是在验证集上达到最佳性能的模型。保存最佳模型的方法是在每次完成验证集上的迭代时如果获得了更好的性能则将模型参数保存到 best.pt 文件中。因此当您使用 best.pt 文件时您将得到整个训练过程中性能最佳的模型。 在应用深度学习模型时通常建议使用 best.pt 文件因为它提供了整个训练过程中性能最佳的模型。但是如果您希望仅仅继续训练模型则可以使用 last.pt 文件模型转换pt转onnxbest.pt转化onnx模型按照这个代码进行转化使用export.py 导出onnx时会输出三个独立的特征图三个尺度的输出而地平线 rdk_x5的工具链只支持单个输出张量格式为 (1,25200,6)。所以直接用官方 export.py 导出的onnx无法直接转换。import torch import sys sys.path.insert(0, .) 加载 best.pt checkpoint torch.load(best.pt, map_locationcpu) if model in checkpoint: model checkpoint[model].float() elif ema in checkpoint: model checkpoint[ema].float() else: model checkpoint.float() model.eval() 设置 Detect 层属性确保导出时拼接所有输出 for m in model.modules(): if hasattr(m, export): m.export True # 打开导出模式 m.onnx_dynamic False # 使用静态网格避免动态形状报错 # 确保不在训练模式 m.training False 创建示例输入并运行一次前向让模型生成初始化的网格 dummy torch.randn(1, 3, 640, 640) with torch.no_grad(): _ model(dummy) 导出 ONNX torch.onnx.export( model, dummy, best.onnx, opset_version11, input_names[images], output_names[output] ) print(best.onnx exported!)三、模型使用打开 yo lov5 文件夹找到 detect.py 文件detect.py 程序里集成了模型所有的使用情况注意我们主要修改 --weights --source这两行调用摄像头修改 --weights 为 runs/train/exp8/weights/last.pt 修改使用模型的位置我们使用 last.pt 模型修改 --source 为 0 修改调用摄像头 0如果有多个可以改 1、2、3修改好后点击 ctrls 保存在 vscode 中选中 yo lo 环境点击运行即可准备预处理的文件codes中存放你转化完的onnx模型和convert_x5.yaml模型转换的配置文件,以及batch_calib_bin.py(该代码是将校准图像转化为二进制图像的)data中存放raw_images用来放背景图没有目标的场景创建一个raw_images文件夹用来存放你的校准数据集在200张照片左右下图是我的路径你也可以存在codes路径下但是batch_calib_bin.py中的路径需要更改一下在data/calibration_data创建一个空白的文件夹用来存放转换好的二进制图像Convert_x5.yaml 文件如下x5的bpu仅有单核不支持双核推理model_parameters: onnx_model: /data/horizon_x5/codes/best.onnx output_model_file_prefix: best working_dir: ./model_output march: bayes-e layer_out_dump: False input_parameters: input_name: images input_type_train: rgb input_layout_train: NCHW input_shape: 1x3x640x640 norm_type: data_scale scale_value: 0.003921568627451 input_type_rt: nv12 calibration_parameters: cal_data_dir: /data/horizon_x5/codes/calibration_data cal_data_type: float32 preprocess_on: False # 手动已将数据预处理为 NCHW float32不要二次处理 calibration_type: default # 或 max可尝试 default 看是否有改善 如果用 max 可以加上 max_percentile: 0.9999 compiler_parameters: compile_mode: latency debug: False core_num: 1 optimize_level: O3batch_calib_bin.py是将你的背景图像转换为二进制的要对原图像进行预处理要不然使用makerbin进行模型转换的时候会出现报错(这里面的路径就是对应你data中我让你创建的文件夹)import os, cv2, numpy as np SRC_DIR /data/horizon_x5/data/raw_images # 原始图片文件夹 DST_DIR /data/horizon_x5/codes/calibration_data # 输出浮点二进制文件 SIZE 640 COLOR (114, 114, 114) # 灰边 BGR def letterbox(img): h, w img.shape[:2] r SIZE / max(h, w) nh, nw int(round(hr)), int(round(wr)) resized cv2.resize(img, (nw, nh)) dh (SIZE - nh) // 2 dw (SIZE - nw) // 2 return cv2.copyMakeBorder(resized, dh, SIZE-nh-dh, dw, SIZE-nw-dw, cv2.BORDER_CONSTANT, valueCOLOR) if not os.path.exists(SRC_DIR): print(f错误原始图片目录 {SRC_DIR} 不存在) exit(1) os.makedirs(DST_DIR, exist_okTrue) count 0 for f in sorted(os.listdir(SRC_DIR)): if not f.lower().endswith((.jpg,.jpeg,.png)): continue img cv2.imread(os.path.join(SRC_DIR, f)) if img is None: continue img letterbox(img) # BGR letterbox img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转 RGB data img.transpose(2,0,1).astype(np.float32) / 255.0 data.tofile(os.path.join(DST_DIR, fcalib_{count:04d}.bin)) count 1 print(f完成共生成 {count} 个校准二进制文件)Dokcer拉取RKD X5镜像打开管理员权限的CMD有NVIDIA显卡无显卡CPU本人下的是cpu版本这个无所谓装GPU版本的话可能还要进行版本配对。我嫌麻烦就装cpu版本了镜像开始下载后是这个画面可能下的比较慢可以更换镜像源千万不要断网docker中断网络后会直接给你重新下载。点击docker里面的齿轮再点击Resources里面可以更改下载路径尽量不要下在C盘解压OE工具包的解压软件官方推荐7-Zip链接7-Zip作者尝试过其他解压软件但是发现解压出来的文件会有问题下载工具包在官方给的文档里链接环境安装 | RDK DOC// 示例版本为 V1.2.8wget -c ftp://x5ftpvrftp.horizon.ai/OpenExplorer/v1.2.8_release/horizon_x5_open_explorer_v1.2.8-py310_20240926.tar.gz --ftp-passwordx5ftp123$%// 更多模型转换示例根据需要进行下载wget -c ftp://oeftpsdk.d-robotics.cc/model_convert_sample/horizon_model_convert_sample.tar.xz --ftp-passwordOeftp~123$%下图是我的路径根据你的挂载路径启动docker。这一步就是将预处理的文件放入X5的环境下进行转换进入后输入hb_mapper --version如果能看到版本信息就说明环境正常可以继续操作输入以下代码可以开始转换图像python3 /data/horizon_x5/codes/batch_calib_bin.py成功如下图输入以下代码可以看到输出层这个是用来检测算子和你的onnx模型适不适配的如果onnx模型有问题会报错。输入以下代码可以开始模型转换,可能需要等一会请耐心等待成功如下图打开文件夹发现有个model_output如下图说明bin模型转换出来了之后就可以上板进行检测了本人用的是MobaXterm直接ssh与X5连接将best.bin和推理代码放入一个文件夹中丢入MobaXterm即可推理代码如下这部分我让ai写的各位读者也可以自己再进行优化cat /rdk_x5_bpu/detect_auto.py EOF import cv2, numpy as np, time, argparse from hobot_dnn import pyeasy_dnn as dnn from scipy.special import expit as sigmoid 配置区域可根据实际场景微调 ANCHORS np.array([[10,13,16,30,33,23], [30,61,62,45,59,119], [116,90,156,198,373,326]], dtypenp.float32) STRIDES np.array([8,16,32], dtypenp.int32) CLASS_NAME jiyu INPUT_SIZE 640 几何过滤参数640×640图上的值 AREA_MIN 8000 # 鱼框合理最小面积 AREA_MAX 200000 # 鱼框合理最大面积 ASPECT_MIN 1.0 # 宽/高下限 ASPECT_MAX 2.5 # 宽/高上限 CENTER_EDGE 0.1 # 中心保留区域比例 (排除边缘) MIN_CONF 0.2 # 最低置信度门槛若模型很好可提高到0.4 网格与锚框预生成 def build_grids(): grids, anchor_grids, stride_arr [], [], [] for i, s in enumerate(STRIDES): s int(s) ny, nx INPUT_SIZE // s, INPUT_SIZE // s yv, xv np.meshgrid(np.arange(ny), np.arange(nx), indexingij) grid np.stack((xv, yv), axis2).reshape(-1, 2).astype(np.float32) grid np.tile(grid, (3, 1)) grids.append(grid) anchor ANCHORS[i].reshape(3, 2).astype(np.float32) anchor np.tile(anchor, (ny * nx, 1)) anchor_grids.append(anchor) stride_arr.append(np.full((grid.shape[0], 1), s, dtypenp.float32)) return (np.concatenate(grids, axis0), np.concatenate(anchor_grids, axis0), np.concatenate(stride_arr, axis0)) GRID, ANCHOR_GRID, STRIDE_GRID build_grids() 预处理 def letterbox(img, sizeINPUT_SIZE, color(114,114,114)): h, w img.shape[:2] r min(size / h, size / w) new_h, new_w int(round(h * r)), int(round(w * r)) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) dh (size - new_h) // 2 dw (size - new_w) // 2 img_out cv2.copyMakeBorder(resized, dh, size - new_h - dh, dw, size - new_w - dw, cv2.BORDER_CONSTANT, valuecolor) return img_out, (dh, dw, r)上板运行这个代码就可以看到画面了