ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLOv5结合CRNN实现车牌识别:从训练到部署实战

YOLOv5结合CRNN实现车牌识别:从训练到部署实战 简介这套车辆牌照识别系统面向计算机视觉、人工智能相关专业的高年级课程实践基于YOLOv5完成车牌区域检测借助CRNN进行字符序列识别并提供GUI交互界面整体方案适合作为毕业设计或课程设计参考。压缩包共110个文件包含53个Python源码、15个YAML配置、预训练权重pth/pt及若干样例图片整体仅8.19MB部署轻量、便于迁移。目前已有40人学习系统代码经过多轮测试在学术评审中获97分识别效果和运行稳定性均有较好表现。内容采用模块化架构检测、识别、界面各层职责清晰注释较完整并附有依赖库清单与说明文档可帮助读者快速理解原理、复现流程并在此基础上进行二次开发与功能扩展。1. 为什么车牌识别要把YOLOv5和CRNN串成一条流水线车牌识别在工程上从来不是“一个模型吃遍天”的任务。你拿一张图直接丢给OCR引擎往往会被车牌以外的文字干扰你只训练一个目标检测网络又只能框出车牌位置读不出上面的字符。实际部署时业界的通用做法是把任务拆成“检测 识别”两级先用YOLOv5定位车牌区域再把裁剪后的车牌图像交给CRNN做序列识别最后用正则或者查表把输出规整成标准车牌格式。这两段各司其职YOLOv5负责“车牌子在哪”CRNN负责“牌子上写了什么”串联起来才是一个能直接对面相机、停车场闸机用的完整系统。这套方案的另一个现实理由是数据获取的难度不同。检测阶段只需要标注“车牌”这一个类别标注成本低识别阶段需要的是字符级别的标注数据量要求高但中文车牌字符集是封闭的各省简称 字母 数字可控性强。所以YOLOv5与CRNN的组合本质上是把两个可以独立迭代的模型拼在一起检测模型换数据集就能迁移识别模型单独增补生僻字样本也不影响检测端。这在工程维护上的价值比“端到端一个网络”要实用得多。Python在这条链路里的角色也很明确YOLOv5官方仓库就是PyTorch实现CRNN也有大量成熟的开源中文OCR实现可以直接改造成车牌字符识别二者都天然运行在Python生态里。GUI部分选择 PySide6 或 Tkinter 取决于你的部署环境后面会细说。这篇文章直接按照“环境搭建 → 车牌检测 → 字符识别 → GUI封装 → 部署优化”的顺序把每一步的命令、参数和坑都过一遍。2. YOLOv5车牌检测从数据集准备到模型训练2.1 YOLOv5环境配置与依赖安装要点先明确一个版本选择问题。YOLOv5官方仓库目前维护的是 v7.0 主线但社区里大量中文车牌教程用的是 v5.0 或 v6.0这会导致超参数文件格式、模型定义文件路径有细微差别。建议直接拉最新版不要纠结旧版本。git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里有个关键坑如果你用的是 NVIDIA 30 系或更新显卡PyTorch 必须以 CUDA 版本匹配的方式安装不能只靠 requirements.txt。正确顺序是先装对应 CUDA 版本的 PyTorch再装 requirements.txt 里的其他依赖。pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt装完之后不要着急训练先跑一个快速的检测验证环境完整性python detect.py --weights yolov5s.pt --source data/images/bus.jpg这条命令的逻辑是加载预训练权重、读取测试图片、输出带框的标注结果。如果它正常运行并在runs/detect/下生成结果图片说明PyTorch、OpenCV、模型文件这条链路已经通了。2.2 车牌数据集标注规范与目录结构车牌检测的数据集标注沿用 YOLO 格式每张图片对应一个同名的.txt文本文件每行代表一个目标格式为class x_center y_center width height其中x_center等值都是相对图片宽高的归一化小数。对于车牌这个单类别场景标注文件写成0 0.512345 0.483210 0.184567 0.095432含义是类别 0车牌、车牌中心点位于图片宽度的 51.23% 处、高度的 48.32% 处、车牌宽度占整图宽度的 18.46%、高度占整图高度的 9.54%。实际标注时建议用 LabelImg 或 LabelStudio不要手写坐标。数据集目录结构必须严格按照 YOLOv5 的约定否则训练时会报 “train dataset not found” 之类的路径错误datasets/ └── plate/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/建议训练集与验证集按 8:1 划分验证集至少留 100 张以上不然置信度和 mAP 的评估结果波动很大看不出模型是否真的收敛。2.3 YOLOv5训练自己的车牌数据集超参数怎么调准备一个plate.yaml文件放在yolov5/data/下train: ../datasets/plate/images/train val: ../datasets/plate/images/val test: ../datasets/plate/images/test nc: 1 names: [plate]训练命令如下python train.py --img 640 --batch 16 --epochs 100 --data plate.yaml --weights yolov5s.pt --device 0--img 640是输入分辨率。这里不建议为了追求速度调到 416因为车牌属于小目标分辨率越低远距离车牌的像素占比越小检测率下降明显。--batch 16是显存允许范围内的安全值--epochs 100对于单类别检测足够再多会有过拟合风险。YOLOv5 的超参数文件是data/hyps/hyp.scratch-low.yaml针对车牌这种单类别任务三个参数值得调整lr0初始学习率默认 0.01。如果你的数据集只有几百张建议降到 0.005否则前几个 epoch loss 容易出现震荡。mosaic数据增强的 mosaic 概率默认 1.0。车牌是规则矩形目标mosaic 增强效果很好不用改。fliplr水平翻转概率默认 0.5。注意中文车牌上的汉字“京A12345”翻转后是反的检测阶段不受影响但如果你后续想把检测和识别做成联合训练这个值建议设成 0。训练完成后看runs/train/exp/下的results.png重点看val/box_loss和mAP0.5两条曲线。mAP0.5 到 0.95 以上说明检测效果已经可用。导出模型用于后续推理python export.py --weights runs/train/exp/weights/best.pt --img 640 --include onnx导出 ONNX 不是必须的但后面做 GUI 推理时ONNX 格式可以用 ONNX Runtime 加载比 PyTorch 原生推理节省不少显存和启动时间。2.4 车牌检测的常见误用与性能瓶颈一个常见误区是把检测置信度阈值设得太高或太低。默认的conf-thres0.25对车牌来说偏保守闸机场景建议设成 0.5 以上因为车牌误检会导致后续 CRNN 识别毫无意义地跑一遍白白增加耗时。另一个坑是 NMS 的iou-thres默认 0.45 够用不需要特意调。性能瓶颈主要卡在预处理和后处理上。YOLOv5 的detect.py包含了 letterbox 缩放、NMS、坐标还原等逻辑这些步骤在 GPU 上很快但在 CPU 上跑推理时反而可能比模型本身更耗时。后面 GUI 部分会给出具体优化方案。3. CRNN车牌字符识别网络结构、训练策略与中文支持3.1 CRNN为什么适合车牌识别网络结构怎么搭车牌识别的核心难点不是英文和数字而是汉字。中国车牌第一个字符是省份简称包括“京、津、冀、晋、蒙、辽、吉、黑、沪、苏、浙、皖、闽、赣、鲁、豫、鄂、湘、粤、桂、琼、渝、川、贵、云、藏、陕、甘、青、宁、新”等加上军用、使馆等特殊车牌字符集大约 60 个左右加上 26 个字母和 10 个数字总共不到 100 类。CRNN 的 CTC 损失函数天然适合这种定长或变长序列识别任务。CRNN 的网络结构分三步卷积层提取图像特征循环层通常用双向 LSTM建模序列上下文最后接一个全连接层做逐帧分类再用 CTC 解码输出最终字符序列。对于车牌这种字符数固定蓝牌 7 个字符绿牌 8 个字符的目标你不需要像通用 OCR 那样处理任意长度文本这让训练收敛速度和准确率都明显优于通用开源中文 OCR 方案。常见的做法是直接魔改开源中文 OCR 项目把输出类别改成自定义的车牌字符集。核心修改在最后全连接层的输出维度上import torch.nn as nn class CRNN(nn.Module): def __init__(self, num_classes, hidden_size256): super(CRNN, self).__init__() # 卷积层部分省略使用标准VGG风格backbone self.rnn nn.LSTM(input_size512, hidden_sizehidden_size, num_layers2, bidirectionalTrue) self.linear nn.Linear(hidden_size * 2, num_classes) def forward(self, x): # x: (batch, channel, height, width) x self.cnn(x) # 输出(batch, 512, 1, width) x x.squeeze(2) # 去掉height维度 x x.permute(2, 0, 1) # 变成(width, batch, 512)时间步排列 x, _ self.rnn(x) x self.linear(x) return xnum_classes是字符类别数加上一个 CTC 空白符如果你的字符集是 80 个汉字 字母 数字这里就填 81。宽度维度的处理是 CRNN 的精华车牌图片的高度被归一化到固定值通常是 32宽度可以变化网络沿着宽度方向逐列提取特征每一列对应一个时间步LSTM 在这个时间序列上建模字符之间的依赖关系。3.2 车牌字符数据集生成与增强策略训练 CRNN 最大的痛点是标注数据不足。真实车牌数据涉及隐私很难大规模获取。业界常用的补充方案是合成数据用程序批量生成逼真的车牌图片。from PIL import Image, ImageDraw, ImageFont import random import string provinces 京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新 chars string.ascii_uppercase string.digits def generate_plate(): province random.choice(provinces) mid .join(random.choices(chars, k6)) return province mid # 例如 京A12345 def render_plate(text, save_path): img Image.new(RGB, (240, 80), (0, 0, 255)) # 蓝底白字 draw ImageDraw.Draw(img) font ImageFont.truetype(simhei.ttf, 48) draw.text((10, 5), text, fill(255, 255, 255), fontfont) img.save(save_path)两张生成图稍微变换一下字体、颜色、亮度、倾斜角度就能制造出上千张训练样本。增强时要注意车牌识别模型的输入是已经裁剪好的车牌图所以不需要做随机裁剪这种检测端增强但随机旋转、透视变换、噪声和高斯模糊对真实场景的适应很有帮助。3.3 CRNN模型训练与CTC损失函数的关键参数训练命令可以直接复用通用的 OCR 训练框架核心参数是学习率和 batch sizepython train_crnn.py --batch_size 64 --epochs 50 --lr 0.0005 \ --train_data dataset/plate/train --val_data dataset/plate/val \ --num_classes 81 --gpu 0学习率 0.0005 是 CRNN 训练的经验值。太大容易在字符序列上震荡太小收敛慢。batch_size 64是一个显存占用和训练速度的折中如果你的显存只有 4G降到 32 不会有明显差别。CTC 损失函数的特性决定了它不需要预对齐。训练样本标注只需要提供真实字符序列比如“京A12345”不需要标注每个字符在图片上的具体位置。这让数据准备简单很多但也带来一个注意点如果批次里的样本字符长度差异过大比如 7 个字符和 8 个字符混在一起CTC 会在短序列上过度惩罚空白输出。所以训练数据里车牌类型尽量按长度分组或者用--max_length参数统一 padding 到 8。训练完成后验证模型效果常用字符准确率和整牌准确率两个指标。整牌准确率才是实际部署时用户感知的指标字符全对但顺序错一个也是识别失败。import torch from net import CRNN def evaluate(model, val_loader): model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: outputs model(images) # CTC解码取每个时间步最大概率类别去重并去除空白 preds decode_ctc(outputs) for pred, label in zip(preds, labels): total 1 if pred label: correct 1 return correct / total这个评估函数会输出整牌准确率。真实场景里车牌识别准确率要求通常在 95% 以上才算可用如果你只有 90%优先检查合成数据是否覆盖了不同光照和倾斜角度而不是急于调网络结构。3.4 车牌字符集覆盖生僻字与特殊车牌的处理中国的车牌体系不止蓝牌和黄牌还有新能源绿牌8位、使馆黑牌特殊字符段、警车白牌和农用绿牌。如果只是做一个演示级别的系统覆盖蓝牌和绿牌就够了但要做成能交付的产品字符集要提前规划。最稳妥的字符集定义方式是建一个字符索引文件char_set 0123456789ABCDEFGHJKLMNPQRSTUVWXYZ \ 京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼 \ 使领学警港澳挂注意这里去掉了字母 I 和 O因为它们和数字 1、0 在车牌字体上极易混淆。这不是网络能解决的问题是车牌标准本身避开的你的字符集也应该同步避开。如果后续要做港澳车牌的识别字符集会多出“港、澳”两个字需要在训练前就确认好范围训练完再加字符需要重新微调整个分类层。4. GUI界面设计与识别流程整合4.1 PySide6还是TkinterGUI框架的选型依据GUI 框架的选择直接决定开发效率和部署体积。Tkinter 是 Python 标准库的一部分不用额外安装适合做简单的图片选择和结果显示。但它有两个明显短板控件风格老旧做不出像样的现代化界面多线程刷新界面时需要小心的线程安全处理处理不好容易界面卡死。PySide6Qt 的 Python 绑定功能完整资源丰富社区里能找到大量现代风控件的现成示例。推荐直接用 PySide6因为车牌识别系统通常还要附带摄像头接入、识别记录查询、参数调节面板这些需求 Qt 的 Model/View 框架和信号槽机制覆盖得非常优雅。pip install pyside6集成摄像头实时识别时PySide6 的QTimer配合 OpenCV 的VideoCapture是标准组合from PySide6.QtCore import QTimer from PySide6.QtGui import QImage, QPixmap class PlateRecognizerGUI(QMainWindow): def __init__(self): super().__init__() self.timer QTimer() self.timer.timeout.connect(self.update_frame) self.cap cv2.VideoCapture(0) def start_camera(self): self.timer.start(30) # 每30ms取一帧约33fps def update_frame(self): ret, frame self.cap.read() if ret: # 传给YOLOv5检测并绘制框 result_frame self.detect_plate(frame) # 转换为Qt可显示的格式 rgb_image cv2.cvtColor(result_frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape qimg QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qimg))这里QTimer的作用是把 OpenCV 的图像采集循环绑定到 Qt 的事件循环上不让界面线程阻塞。如果你不这样做摄像头画面会直接卡在“未响应”状态。4.2 把YOLOv5和CRNN封装成可调用的识别类训练好的模型不能直接拿detect.py跑GUI 需要的是一个可以输入图片、输出车牌号和置信度的 Python 类。封装的核心是把模型加载放到__init__里避免每次识别都重新加载权重。import cv2 import torch import numpy as np class PlateRecognizer: def __init__(self, yolo_weights, crnn_weights, device0): self.device torch.device(fcuda:{device} if torch.cuda.is_available() else cpu) # 加载YOLOv5模型 self.detect_model torch.hub.load(ultralytics/yolov5, custom, pathyolo_weights, deviceself.device) self.detect_model.conf 0.5 self.detect_model.iou 0.45 # 加载CRNN模型结构定义从外部导入 self.recognize_model CRNN(num_classes81) self.recognize_model.load_state_dict(torch.load(crnn_weights, map_locationself.device)) self.recognize_model.to(self.device).eval() def recognize(self, image): # 检测阶段 results self.detect_model(image) boxes results.xyxy[0].cpu().numpy() plate_texts [] for box in boxes: x1, y1, x2, y2, conf box[:5] plate_crop image[int(y1):int(y2), int(x1):int(x2)] # 识别阶段预处理到CRNN要求的尺寸 plate_crop cv2.cvtColor(plate_crop, cv2.COLOR_BGR2GRAY) plate_crop cv2.resize(plate_crop, (240, 80)) plate_crop plate_crop.astype(np.float32) / 255.0 plate_crop torch.from_numpy(plate_crop).unsqueeze(0).unsqueeze(0) with torch.no_grad(): outputs self.recognize_model(plate_crop) text ctc_decode(outputs) plate_texts.append((text, conf)) return plate_textsctc_decode是 CRNN 推理的核心逻辑对每个时间步取 softmax 后的最大概率类别索引合并连续重复字符最后去掉空白符索引。这个后处理步骤虽然只有十几行代码却是最容易写错的地方常见的错误是忘了合并连续重复字符导致“京A 12345”被识别成“京AA12345”。4.3 识别结果可视化绘制检测框与置信度GUI 界面里展示识别结果的关键是在原图上绘制合适的可视化信息。不仅仅是画一个矩形框还要把识别出来的车牌字符和置信度同时标注出来方便用户直接核对。def draw_results(frame, results): for text, conf in results: # 假设results里已经包含坐标信息 x1, y1, x2, y2 text[box] cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) label f{text[plate]} {conf:.2f} # 标签背景避免文字和背景混在一起 cv2.rectangle(frame, (x1, y1 - 25), (x2, y1), (0, 255, 0), -1) cv2.putText(frame, label, (x1 5, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0), 2) return frame这里的细节是绘制文字前先用一个矩形填充背景否则深色车牌图片上绿色文字可能看不清。另外conf是检测置信度识别模型输出的 softmax 概率也可以显示但通常只展示检测置信度就够了避免界面信息过载。实际界面布局上建议左侧放视频画面右侧放识别结果的表格车牌号、时间、置信度底部放操作按钮“打开图片”“打开摄像头”“停止识别”“导出记录”。不要做太多花哨的动画部署环境下稳定比好看重要。4.4 批量识别从图片文件夹到CSV导出GUI 之外批量识别是实际运维的高频需求。比如停车场某个时段抓拍了大量图片需要离线识别并导出报表。这个功能可以做成一个独立的处理流程不依赖 GUI 界面。import os import csv from pathlib import Path def batch_recognize(folder_path, output_csv): recognizer PlateRecognizer(best.pt, crnn_best.pth) results [] for img_path in Path(folder_path).glob(*.jpg): img cv2.imread(str(img_path)) plates recognizer.recognize(img) for plate, conf in plates: results.append({image: img_path.name, plate: plate, confidence: conf}) with open(output_csv, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[image, plate, confidence]) writer.writeheader() writer.writerows(results) print(f结果已保存到 {output_csv})CSV 导出时编码一定要指定utf-8否则 Windows 上用 Excel 打开中文车牌会乱码。如果想在 Excel 中直接打开不出现 BOM 问题可以改用utf-8-sig编码。批处理场景下需要注意内存管理读取一张处理一张不要把整个文件夹的图片一次性读进内存。5. 模型推理加速与ONNX部署5.1 为什么要把PyTorch模型转为ONNX格式GPU 环境下 PyTorch 原生推理速度尚可但 CPU 或边缘设备上 PyTorch 的推理效率明显偏低。ONNX Runtime 针对 CPU 做了大量算子融合和优化在 Intel 平台的推理速度通常比 PyTorch 原生快 1.5 到 3 倍。车牌识别系统经常部署在闸机工控机上这类设备往往只有 CPU 没有 GPU所以模型导出为 ONNX 是实用选择。另外透过 ONNX 还可以进一步转为 OpenVINO 或 TensorRT 格式分别适配 Intel 和 NVIDIA 平台。但这个步骤不是必须的只要 ONNX Runtime 推理速度够用就不需要引入额外的部署框架。5.2 ONNX模型导出与精度验证YOLOv5 的导出命令很简单:python export.py --weights best.pt --img 640 --include onnx --simplify--simplify参数会调用 onnx-simplifier 对计算图做常量折叠和冗余节点消除导出的模型更小、推理更快。CRNN 的导出不能用现成脚本需要自己写一段导出代码import torch from net import CRNN model CRNN(num_classes81).eval() model.load_state_dict(torch.load(crnn_best.pth, map_locationcpu)) dummy_input torch.randn(1, 1, 80, 240) # batch1, 灰度图, 高80, 宽240 torch.onnx.export( model, dummy_input, crnn.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}} )动态轴设置的意义是让 ONNX 模型支持不同的 batch size方便后续批量推理。导出后必须验证精度对齐import onnxruntime as ort import numpy as np ort_session ort.InferenceSession(crnn.onnx) onnx_output ort_session.run(None, {input: dummy_input.numpy()}) torch_output model(dummy_input).detach().numpy() print(f最大误差: {np.abs(onnx_output[0] - torch_output).max()})最大误差在 1e-5 级别是正常的如果误差超过 1e-3 说明模型结构里有算子不支持 ONNX 转换通常出在 LSTM 的初始状态处理上需要排查导出兼容性。5.3 ONNX Runtime推理性能对比与参数调优用 ONNX Runtime 做推理时打开一些优化选项能把速度再往前推一步。关键参数是intra_op_num_threads和execution_modeimport onnxruntime as ort session_options ort.SessionOptions() session_options.intra_op_num_threads 4 session_options.execution_mode ort.ExecutionMode.ORT_PARALLEL detect_session ort.InferenceSession(yolov5s.onnx, sess_optionssession_options) crnn_session ort.InferenceSession(crnn.onnx, sess_optionssession_options)线程数设置不是越大越好。对于 YOLOv5 这种算子密集的模型4 到 6 个线程通常达到峰值性能对于 CRNN 这种含 LSTM 的序列模型2 到 4 个线程更合适因为 LSTM 是逐步计算的过程过高的并行度反而增加线程调度开销。实际调优方法很简单写一个循环跑 100 次推理取平均时间然后遍历线程数参数找到最优点。YOLOv5 转 ONNX 后有一个额外的好处是移除预处理中的 letterbox 时更容易控制。ONNX Runtime 版推理可以直接把输入尺寸固定为 640×640减少一次计算量。5.4 实际部署时的显存与内存限制即使在 GPU 机器上部署也要注意显存占用。YOLOv5 默认用--half参数可以做半精度推理显存占用减半速度提升明显。ONNX Runtime 也支持 float16 模型但需要确认目标 GPU 是否支持。老旧的 Tesla P40 不支持 float16 加速用 half 反而变慢这是硬件限制不是软件问题。内存方面批量识别时如果图片分辨率很高比如 4096×2160图像缩放和 BGR 转 RGB 会产生多个中间数组。用cv2.dnn.blobFromImage做预处理可以减少一次拷贝但可读性较差。稳妥做法是限制单批处理的图片数量或者直接用 cv2 的imread后按比例缩放一次再进模型。6. 误检与漏检的工程化排查手段车牌识别系统上线后代码层面的问题不多了真正花时间的是分析识别失败的案例。建议从第一天就给系统加上日志记录功能把每次识别的结果、置信度和图像缩略图都存下来。很简单的做法是把检测框截图保存再配上识别结果这样出现问题时不用对着原始视频一帧帧找效率高很多。针对常见失败模式可以建立一个简单的分类排查表。识别结果为空或字符乱码大概率是 CRNN 的训练数据覆盖不足优先补充对应字符的合成样本。检测框位置准确但识别结果为空白检查 CRNN 图像预处理和字符集对齐问题。检测框偏移严重导致识别不准检查 YOLOv5 的输入分辨率远处车牌容易被裁掉一半。误检率偏高时优先调高置信度阈值然后检查是不是把车灯、车标等图案当成了车牌。日志分析中加一个针对字符级混淆的统计也会提高效率。比如“京A12345”被识别成“京A12346”这类数字尾部错误说明 5 和 6 在合成数据生成时字体渲染差异不够应该在生成训练数据时多增加几种字体和笔画粗细变化。汉字识别错误则优先检查省份简称的样本量是否均衡常见的“豫”和“鄂”字形接近训练数据不足时容易混淆。最后按照车牌颜色再分一层统计。蓝牌准确率通常高于其他类型因为蓝牌训练数据多、场景规范。绿牌和黑牌的准确率如果低于 90%单独为它们扩充训练集比在通用数据上盲目增加样本更有效因为不同车牌类型的字体、对比度和拍摄环境差异都很大混在一起训练反而会让网络学到错误的共性特征。本文还有配套的精品资源点击获取
返回列表