简介:这套基于YOLOv8与LPRNet的车牌识别系统,将车牌目标检测与字符识别串联成完整流程,主要面向毕业设计、课程设计和期末大作业,代码内含详细注释和训练好的模型权重,新手也能按说明快速部署。压缩包共60个文件、约31.36MB,覆盖Python训练推理脚本、Vue前端页面、JSON配置、PyTorch权重、Docker部署文件、Markdown说明文档等类型,目录清晰便于检索。LPRNet模块提供车牌样本生成、批量图像处理、标注制作、数据集划分和训练脚本,YOLOv8模块包含检测模型训练与调用逻辑,Vue模块实现可视化操作界面,并配有Flask后端服务,完整覆盖从数据准备到模型部署的整个链路。目前已有232人学习下载,项目经过严格调试可直接运行,界面美观、操作简单,适合需要快速搭建演示系统或深入理解车牌识别原理的开发者借鉴。
1. 基于 YOLOv8 和 LPRNet 的车牌识别系统:源码加训练好的模型,毕设怎么拿它落地
车牌识别系统在毕业设计里是常青树,但难点不在“识别”两个字,而在检测与识别两段怎么衔接。“基于 YOLOv8 和 LPRNet 的车牌识别系统”给出的是一条完整可跑的链路:YOLOv8 负责在图像里框出车牌区域,LPRNet 再把车牌区域解码成字符串;配上训练好的模型权重,你拿到的不只是演示视频,而是能改、能换数据、能重新训练的一套代码。这套方案适合正在做毕设的学生、想快速搭监控或道闸原型的工程师。它能直接回答三个问题:车牌在哪、车牌是什么、换一个场景还要改哪些参数。
2. 为什么检测选 YOLOv8、识别选 LPRNet:两段网络的取舍逻辑
先解决选型问题。车牌识别端到端方案不少,有直接用一个网络输出的,也有用 CRNN 或 PaddleOCR 做识别的,但“YOLOv8 检测 + LPRNet 识别”是工程落地里最常见的组合之一,不是因为它时髦,而是两段网络的专精度恰好互补。这一章拆开看每段网络的职责,顺带说清楚那些看着能省一步的替代方案为什么不能随手换。
2.1 检测端:YOLOv8 把“车牌在哪”当作回归问题,一张图一次推理出框
YOLOv8 的检测头是 anchor-free 的,在 640×640 输入下一次性输出目标类别、框中心和宽高,推理链路里没有候选区域生成这一步。相比之下,Faster R-CNN 这类两阶段检测器在车牌这种稀疏目标上精度足够,但每张图的耗时对现场演示很不友好;要在答辩现场拿 CPU 或入门级显卡跑实时视频,YOLO 的性价比就体现出来了。YOLOv8 的结构主线是输入图像 → Backbone(C2f 模块)提取特征 → Neck(PAN-FPN)融合多尺度 → Head 输出结果。社区流传的 yolov8 网络结构图大多是模块级示意,想看自己训练的那份具体结构,装好库后直接打印模型对象,就能把每个模块的参数列出来,不用去翻别人画的图。
对车牌检测来说,通常只靠大、中尺度特征就够了,但几十米外的监控画面里车牌是很小的目标,这时反而要依赖 640 或更高的输入尺寸来兜底。这正是“YOLOv8 检测小目标会翻车”这条血泪经验的来源,后面避坑章还会细说。再对比传统思路:HSV 颜色分割、形态学、轮廓提取也能做车牌检测,蓝牌区域的色调确实是强特征,但夜间偏色、新能源绿牌、反光、雨雪泥污都会让阈值完全失效。颜色特征在可控环境里好用,到真实监控场景就变成玄学调参。YOLOv8 的本质是把颜色、纹理、位置关系一起交给卷积层学,训练数据覆盖得越杂,鲁棒性越好。
2.2 识别端:LPRNet 用 CNN+RNN+CTC 处理不定长字符,是车牌场景的专用解法
检测输出了车牌框,但框里是什么字符要靠第二段网络。LPRNet 是2018年前后提出的端到端车牌识别网络,核心结构是卷积骨干加循环层再加 CTC 解码。它不需要先把字符切出来,而是把整块车牌图像当作一个字符序列输入,网络输出所有字符位置的概率分布,再用 CTC 解码得到最终字符串。对比通用 OCR(PaddleOCR、Tesseract 这类方案):通用 OCR 的字符集覆盖几千个字,但车牌字符集小且规则固定,第一位是汉字省份简称,后面是字母和数字,蓝牌总共 7 位、新能源车 8 位。通用 OCR 在车牌这种强结构化序列上经常输出多余字符或乱序,而 LPRNet 的标签体系可以直接约束字符集和序列长度。另一个现实因素是模型体积:LPRNet 只有几 MB 量级,CPU 推理很快,普通笔记本就能实时跑。
| 对比项 | LPRNet | CRNN | 通用 OCR(PaddleOCR 等) |
|---|---|---|---|
| 字符集 | 车牌专用,可自定义 | 自定义 | 数千常见汉字 |
| 车牌结构化约束 | 强(按序列位置解码) | 弱 | 基本无 |
| 模型体积 | 小 | 中 | 偏大 |
| CPU 推理速度 | 快 | 中等 | 偏慢 |
| 换训练数据成本 | 低 | 低 | 中高 |
LPRNet 的官方权重和实现方式在开源社区里流传很广,所以毕业设计里常见做法是:检测端用现成 YOLOv8 库,识别端自己组 LPRNet 训练脚本,两头都能拿出能讲原理的代码。相比之下,把两段都换成一个端到端网络,表面上省了衔接步骤,但网络结构、损失函数、解码逻辑全揉在一起,答辩时反而更难讲清楚每一段在干什么。
2.3 衔接:检测框裁剪、透视矫正、灰度化的顺序别乱,否则识别率白丢
把两段网络串起来还不够。真实监控画面里车牌常常是斜的——车辆视角、相机安装高度、转弯时的投影都会让车牌变成梯形。LPRNet 训练时面对的是近似正视角的规整车牌,直接把斜框内容送进去,识别率会明显下降。所以中间要加一步透视矫正,这也是源码里那段看着像坐标变换的代码实际在干的事。
常见做法是:按检测框外围加一点 margin 把区域切出来,用 OpenCV 找车牌四角点(车牌本身是矩形,投影后是四边形),做透视变换拉成标准矩形,再灰度化、归一化、缩放到 LPRNet 的输入尺寸(经典配置是 94×24)。顺序之所以不能乱,是因为透视变换涉及双线性插值,在彩色空间做完再转灰度,插值产生的颜色误差更小;反过来先灰度再几何变换,角点检测和插值都会受单通道信息量影响。
import cv2 import numpy as np def rectify_plate(plate_img, pts, dst_w=94, dst_h=24, margin_ratio=0.05): # pts: 车牌四角点,顺序为左上、右上、右下、左下 # margin_ratio: 矫正前对四边形做外扩比例,避免字符切边 pts = np.array(pts, dtype=np.float32) center = pts.mean(axis=0) pts = center + (pts - center) * (1 + margin_ratio) dst = np.array([[0, 0], [dst_w - 1, 0], [dst_w - 1, dst_h - 1], [0, dst_h - 1]], dtype=np.float32) M = cv2.getPerspectiveTransform(pts, dst) out = cv2.warpPerspective(plate_img, M, (dst_w, dst_h)) return out代码里的 pts 是检测模型输出的四个角点,如果没有角点输出,就用矩形框的四角近似。margin_ratio 是经验值,太小会把字符切掉,太大会把隔壁车的车灯包进来,常见取值在 3% 到 8% 之间。dst 尺寸要对齐 LPRNet 的训练输入;如果用的是别的输入尺寸训的权重,这里必须同步改,不然推理结果全是乱码。这段代码本身不复杂,但它往往是决定识别率最关键的一段。
3. 跑通最小链路:环境配置、数据转换和已训练权重的使用
拿到源码包之后,第一件事不是上去就改训练参数,而是把“检测 → 矫正 → 识别”的推理链路先跑通,让你手里的训练好的模型在真实图片上出结果。能跑通就说明环境和代码版本是配套的,后续改数据、改参数都在这条链路里迭代。这一章按环境安装、数据格式、推理脚本的顺序来,按步骤走不会卡壳。
3.1 环境配置:ultralytics 库、PyTorch 和 Python 版本怎么凑一套
YOLOv8 现在的使用方式集成在 ultralytics 库里,一行 pip 命令就能装完,不建议再从源码仓库克隆去编译,能省掉一堆依赖冲突。LPRNet 的实现通常是一两个自组织的 Python 文件,不依赖重型框架,只要 PyTorch 版本兼容就行。所以环境本质上就两件事:装 ultralytics 库,装和显卡驱动配套的 PyTorch。没有 conda 的环境用 venv 建虚拟环境也一样,关键是别把包装到系统 Python 里。
conda create -n plate python=3.10 -y conda activate plate pip install ultralytics opencv-python # 显卡驱动比较新时直接装默认 wheel 即可,装完验证 cuda 是否可用 pip install torch torchvision装完先做一次纯导入验证,不要一上来就下载模型跑推理。在一台全新机器上最容易翻车的点是 PyTorch 的 CUDA 版本和驱动不匹配,导致torch.cuda.is_available()返回 False。GTX 1660 Ti 这种 6GB 显存的卡,装对应 CUDA 版本的 wheel 就行,不必追新驱动。
python -c "import ultralytics, torch; print(ultralytics.__version__, torch.__version__, torch.cuda.is_available())"最后一项输出 True 说明显卡环节正常。如果是 False,先查驱动版本,再重装对应 CUDA 的 PyTorch,不要急着怀疑别的依赖。
提示:首次用 YOLO 跑推理时,如果权重文件不在本地,ultralytics 会自动尝试联网下载;断网环境记得先把 .pt 文件放到工程 weights 目录再加载。
3.2 数据准备:把 VOC/CCPD 标注转成 YOLOv8 需要的 txt 格式
训练检测模型前,数据得先变成 YOLOv8 认得的格式:每张图对应一个同名 txt,每一行是class_id x_center y_center width height,坐标全部归一化到 0 到 1 区间。很多车牌数据集(包括部分开源的 VOC 版本)用的是 XML 标注,CCPD 用的是 JSON 里的坐标,都需要先转换。下面给出从 VOC XML 转 YOLO txt 的脚本,CCPD 的转换逻辑类似,只是解析 JSON 的那一段不同。
import os import xml.etree.ElementTree as ET def voc2yolo(xml_dir, out_dir, categories={"plate": 0}): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue root = ET.parse(os.path.join(xml_dir, xml_file)).getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) lines = [] for obj in root.iter("object"): cat = obj.find("name").text if cat not in categories: continue # 只保留车牌类,其他目标直接跳过 box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) cx = ((x1 + x2) / 2) / w cy = ((y1 + y2) / 2) / h bw = (x2 - x1) / w bh = (y2 - y1) / h lines.append(f"{categories[cat]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") txt_path = os.path.join(out_dir, xml_file.replace(".xml", ".txt")) with open(txt_path, "w") as f: f.write("\n".join(lines) + "\n") print("done") voc2yolo("annotations/", "labels/")这段代码只保留名字为 plate 的目标并转成归一化坐标。w 和 h 取自 XML 里记录的原图尺寸,不是你想当然的 640——用错尺寸会把框归一化得全部偏掉。categories 字典把“plate”映射成类别编号 0,如果你的数据里还有别的类,按自己的类别顺序扩展即可。转换完建议随机抽三张图,把 txt 的行还原成原图坐标画一遍框,肉眼确认框没偏,再进训练。这一步是在给后面的训练“攒后悔药”,省掉它,等训练跑了两百个 epoch 才发现数据有问题,返工成本就高了。
3.3 用训练好的模型先跑一次端到端推理
环境通了、数据格式理解了,就该把训练好的权重用起来。检测权重(通常是一个 best.pt)和识别权重(LPRNet 的 .pth)是两份文件,推理时要分开加载。下面的脚本演示一次端到端流程:检测 → 取框 → 矫正 → 识别。注意 LPRNet 的加载方式取决于你手上源码包的模型定义,这里用的是状态字典通用写法。
import cv2 import torch from ultralytics import YOLO detect_model = YOLO("weights/best.pt") # 车牌检测权重 lpr_model = torch.load("weights/lprnet.pth", map_location="cpu") lpr_model.eval() img = cv2.imread("test/001.jpg") results = detect_model(img, conf=0.25) # conf 太低会出现大量误检框 for r in results: for box in r.boxes.xyxy.cpu().numpy(): x1, y1, x2, y2 = [int(v) for v in box] plate_crop = img[y1:y2, x1:x2] # 先按矩形框裁剪 # 透视矫正在这里调用 2.3 节的 rectify_plate(plate_crop, corners) # 之后做灰度化、归一化,按 LPRNet 仓库约定的预处理送入网络 # pred_chars = decode_lprnet(lpr_model, plate_crop) # print(pred_chars)加载方式用的是状态字典通用写法,具体到手里的 LPRNet 权重,要看源码包的模型定义类名和预处理函数名,这是唯一需要对齐的地方。conf=0.25 是经验值:调太高会把后景里的模糊车牌漏掉,调太低会把路牌、广告牌上的文字当车牌,蓝牌正常场景 0.25 起步。推理时如果 CUDA 报错,把 map_location 保持为 "cpu" 先排查逻辑问题,性能是最后一步才考虑的。先跑通再优化,这是所有模型项目通用的顺序。
4. 自己训练:YOLOv8 检测命令与 LPRNet 识别训练的必调参数
跑通推理只是开始,毕业设计如果要体现工作量,通常要把训练过程完整走一遍。这一章给的是两条训练路径的具体参数:检测侧用 YOLOv8 官方命令直接起训,识别侧用 LPRNet 的训练循环,重点说清楚哪些参数动了会改变结果走向。
4.1 YOLOv8 训练命令:一条命令里的四个必改参数
YOLOv8 训练不用自己写训练循环,官方 CLI 一行命令就能跑。以自己转换好的数据集为例,先建一个数据配置 YAML:path 指向数据集根目录,train 和 val 指向划分好的图片目录,names 是类别名列表。然后执行下面的命令。这是 yolov8 训练自己的数据集最标准的起手式。
yolo detect train data=plate.yaml \ model=yolov8s.pt \ epochs=120 \ imgsz=640 \ batch=16 \ lr0=0.01 \ device=0四个必改参数的含义要清楚:
- model 指向
yolov8s.pt表示用 COCO 预训练权重继续训,常见做法是拿预训练权重起步而不是从零开始,收敛快,最终 mAP 通常也更高。 - imgsz 输入尺寸,640 是平衡点;车牌在画面里占比小时,提到 736 或 960 会显著改善小目标漏检,代价是显存和训练时间翻倍。
- batch 受显存制约。GTX 1660 Ti 6GB 上,yolov8n + imgsz 640 配 batch 8 是能顺畅跑完的组合,s 模型要把 batch 降到 4。
- lr0 建议保持默认 0.01,改成 0.001 不算错,但 scheduler 的 warmup 阶段会变长,迭代数不变时后期 mAP 常常上不去。
训练过程里日志会实时打印 box_loss、cls_loss 和 mAP50。看训练有没有翻车,优先看验证集 mAP,而不是训练集 loss。训练集 loss 下降只能说明模型在背数据,验证集 mAP 才是泛化能力的反映。
4.2 LPRNet 训练:字符集、序列标签和 CTC Loss 三个绕不开的点
LPRNet 的训练代码不长,但三个细节决定成败:字符集文件(每行一个字符,顺序就是标签索引)、序列标签的生成方式、CTC Loss 的 blank 处理。中国车牌的字符集里没有字母 I 和 O,训练的时候如果混进这些字符,解码结果会大面积错乱。下面给出训练循环的核心片段,框架是 PyTorch 常见写法。
import torch import torch.nn as nn char_set = list("0123456789ABCDEFGHJKLMNPQRSTUVWXYZ京沪粤浙苏鲁豫冀川渝陕闽鄂湘皖晋辽吉黑桂云贵甘青藏蒙宁新琼港澳使领") ctc_loss = nn.CTCLoss(blank=len(char_set), zero_infinity=True) def encode_label(text): seq = [char_set.index(c) for c in text] return torch.tensor(seq, dtype=torch.long) # 每个 batch 里按最长车牌做 padding,长度差交给 CTC 的输入长度参数处理 for images, texts, seq_lens in train_loader: logits = lpr_model(images) # 输出形状 (T, N, C) loss = ctc_loss(logits, [encode_label(t) for t in texts], [logits.shape[0]] * len(texts), seq_lens) optimizer.zero_grad() loss.backward() optimizer.step()logits 的第一个维度是时间步 T,对应车牌图像的横向切分步数;N 是 batch。CTCLoss 的四个参数分别是网络输出、标签序列、输出长度和标签长度,少传一个都会在维度上报警。blank 设为字符数本身,表示空白位,不能和任何真实字符索引重叠。字符集顺序固定后不要再改,否则之前训好的权重和标签对不上,这也是常见的返工原因之一。
4.3 训练结果评估:mAP 只能说明检测,车牌号级准确率才能说明系统
检测模型的 mAP 再高,识别错一个字符,整块车牌还是错。所以切到评估阶段,别只看检测指标。常见做法是批量跑测试集,把整条链路的结果和标注文本做比对,统计“完全一致的车牌数 / 总车牌数”,这就是车牌号级准确率。字符准确率数值会更高一些,但对答辩没有意义——用户要的是整块牌读出来。
YOLOv8 训练完成后,runs/detect/train目录里的results.csv记录着每个 epoch 的损失和 mAP,直接用 pandas 读出来画两条曲线就能放进毕设论文里,不必额外接 TensorBoard。这也是 yolov8 画损失函数曲线图最省事的方式。
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") plt.plot(df["epoch"], df["val/box_loss"], label="val box loss") plt.plot(df["epoch"], df["val/cls_loss"], label="val cls loss") plt.legend() plt.savefig("loss_curves.png")val/box_loss这类列名在不同版本里可能有细微差异,跑之前先打印df.columns确认列名。如果 val loss 曲线在后期不降反升,说明模型开始过拟合训练集,优先检查数据是不是太单一,而不是无脑加 epochs。检测做完了,识别模型也要按同样的思路单独评估:识别模型的评估集要按“车牌的省份分布、蓝绿牌比例、白天黑夜”切,不能只从训练集里随机抽。
5. 避坑:从漏检到识别错,五条血泪记录
这一章讲那些表面上看不出来、实际会吃掉大量时间的坑。每一条都是真实翻车现场,按“现象 → 原因 → 解决”写,方便直接对照排查。很多坑不是模型结构的问题,而是数据和衔接细节的问题,这也正是新手和熟手拉开差距的地方。
5.1 蓝牌识别正常,新能源绿牌全部识别错
现象:蓝牌训练得挺好,换一批绿牌图,检测没问题,识别结果乱码率高。明明同样是汉字加字母数字,绿牌就是大面积出错。
原因:训练集的字符集和样本比例失衡。绿牌是 8 位(前 6 位加后 2 位),蓝牌是 7 位,LPRNet 如果只在蓝牌上训练,模型对序列长度的先验就是 7 位;绿牌的 D、F 字母前缀和更长的序列会让解码结果整体偏移。
解决:混入绿牌数据时,把绿牌单独做一个标签子集,字符集里加入 D、F,同时按 3:1 到 4:1 的比例混合蓝绿,别让绿牌数量太少。LPRNet 是数据驱动很强的网络,数据量才是决定性因素。如果时间紧,可以只对绿牌做一次微调,用蓝牌数据训练出的权重做初始点,学习率降到 0.0001 量级,训练几十个 epoch 就够。
5.2 检测框比车牌大一圈,识别率反而下降
现象:检测框不紧贴车牌,两边多出车身的油漆或车灯,矫正后字符被拉伸变形,识别结果对不上。
原因:YOLOv8 的框是矩形目标框,车牌有透视梯形时目标框天然会比实际区域宽;直接裁剪后送识别网络,LPRNet 的输入比例被破坏。矩形框没法表达梯形车牌,这就是检测和识别之间最容易忽略的精度损耗点。
解决:检测后不要直接裁剪,先用矩形框内的内容做角点检测(车牌本身是亮色区域,轮廓明显),得到四个角点再做 2.3 节的透视矫正,再裁剪。如果角点检测不稳定,就把矩形框横向外扩 5% 到 8% 后再矫正,宁多勿少,字符被切是比背景多更严重的问题。外扩比例也可以写进配置文件,方便对不同场景微调。
5.3 训练损失一路下降,验证 mAP 却卡在原地
现象:训练日志里 loss 一直在降,但每轮 val mAP 停在同一数值,看起来像是训练“假死”。排除了数据问题后,这种现象还是会出现。
原因:常见有两个。一是学习率没起调,模型在初始预训练权重附近打转,val mAP 被锁死;二是数据集划分出了问题,验证集图片和训练集来自同一个视频序列,模型过拟合到拍摄角度和光照,验证集自然“看起来还行”。
解决:先确认 lr0 是否生效,ultralytics 会在训练开头用 warmup 从更小学习率爬升,如果 lr0 设到 0.0001 以下,120 个 epoch 未必能从 warmup 里出来;把 lr0 调回 0.01 试一个短训,看 val mAP 是否松动。数据划分按“按天、按地点”切,不要按视频帧随机切。模型训练的评估指标只有在自己会动的条件下才有意义,这是最容易忽略的前提。
5.4 在自己机器上能跑,拷到另一台机器上一加载权重就报错
现象:同一份 best.pt,自己电脑上推理正常,换台机器torch.load报错或产生张量形状不匹配。代码没改,环境变了就崩。
原因:主要是 PyTorch 和 CUDA 版本不一致,权重里保存的模型结构和运行环境对不上;另一个常见原因是加载时没写map_location,状态字典默认会尝试往 CUDA 上放,目标机器没有对应设备就崩。
解决:加载权重时统一用torch.load(path, map_location="cpu"),再手动执行model.to(device),不要在 load 这一步依赖默认设备。两台机器之间迁移时,把 requirements.txt 里的 torch 版本固定住,比反复排查玄学报错省时间。这个坑在环境配置章节其实就能提前避免,多花两分钟写死版本号,后面少折腾半天。
5.5 夜间/反光场景漏检,白天模型拿过去不灵
现象:白天训练的模型,在夜晚监控、强光反射场景漏检率飙升,检测框时有时无,识别也跟着全错。
原因:训练集基本是白天数据,光照增强没做透。YOLOv8 对光照变化有一定鲁棒性,但车牌反光、车灯过曝、路面阴影这些强干扰会让特征分布明显偏移。实拍场景里的光照分布和公开数据集差距越大,问题越明显。
解决:训练时开启随机亮度、对比度增强(ultralytics 的hsv_h、hsv_s、hsv_v参数),并往数据里加夜间、逆光样本。更省事的方法是直接在该场景下补 200 到 500 张实拍图微调,比任何数据增强都见效快。补拍时注意一个关键点:夜间车牌的明亮区域是反光造成的,样本里要覆盖不同角度下的反光形态,而不是只拍正对着相机的情况。
6. 进阶:把链路压到 RK3588 之前,先做 ONNX 导出与定量验证
如果用这套系统做边缘设备(不少毕设会往 RK3588 这类板子上部署),第一步是把 PyTorch 模型导成 ONNX,再用 RKNN 工具链转成板端格式。检测侧的命令很简单:yolo export model=weights/best.pt format=onnx opset=12。LPRNet 的导出要手动处理动态维度:把 batch 固定为 1,时间步 T 保持和训练一致,否则转换工具经常在维度推导上报错。
导出后先做精度比对,拿 100 张测试图统计量化前后的车牌号级准确率。单纯为了“跑起来”而直接量化,遇到精度崩了会很难定位是量化引入的误差还是前处理不一致。我一般先跑 fp16,确认精度不掉,再试 int8;int8 对车牌这种小目标通常会有几个百分点的掉点,如果训练集本身覆盖不全,这个掉点会被放大成不可用。模型的瘦身也可以在部署阶段做:检测侧从 s 换成 n,帧率能提一截,识别侧 LPRNet 本来就很轻,一般不用再动。
我踩过的一个坑是急着把 int8 模型烧进板子,结果精度崩了,最后查下来不是算子问题,而是校准集太稀疏。现在每次转换前我都先定一张固定的测试集和准确率基线,再动量化。希望帮到你。
本文还有配套的精品资源,点击获取