资讯动态

Python车牌识别计费系统:YOLOv5s+CRNN实时闭环部署

发布时间:2026/10/28 1:08:58 来源:尧图企业网站定制
简介本资源是一套基于Python开发的智能停车场车牌识别与计费系统完整实现面向计算机专业学生、AI初学者及中小型智慧园区项目开发者解决车辆出入自动识别、动态计费、营收可视化与车位预警等实际管理问题。压缩包共2003个文件主体为1777个Python源码含核心识别逻辑、GUI界面、计费算法与百度AI接口封装辅以49个配置与说明类txt文件、12个PDF文档含详细使用手册、8个JSON/JS/CSS前端资源用于统计图表渲染整体达189.59MB结构清晰、模块解耦度高。已有248人学习下载资源包含可直接运行的PyCharm工程、百度AI密钥集成指引、多场景界面截图入场/出场/收入柱状图/满预警提示及配套Word使用说明文档覆盖从环境配置、摄像头调用、车牌OCR识别到数据持久化与业务逻辑闭环的全流程实践。1. Python 实现智能停车场车牌识别计费系统不是Demo是能接真实摄像头、跑通从抓拍到扣费闭环的可部署源码包你是不是也见过一堆标着“车牌识别”的 GitHub 项目——点进去全是 Jupyter Notebook 里读一张静态图、调用 OpenCV 写几行轮廓检测最后输出个plate: 京A12345就算完事这种代码连测试集都没过更别说接 IPC 摄像头、处理雨雾反光车牌、应对夜间低照度、支撑多车连续入场/离场计费逻辑。而这份「Python 实现智能停车场车牌识别计费系统」是某高校实验室联合本地停车运营方落地验证过的完整工程包它包含实时视频流接入模块支持 RTSP/USB、YOLOv5s 轻量车牌检测模型已量化为 ONNX、CRNN 端到端车牌识别模型含中文字符集 finetune、基于 SQLite 的本地计费数据库支持时段阶梯计费、VIP 免费时长、超时自动加价以及带 Web 管理界面的 Flask 后端含车辆进出记录查询、收费明细导出、设备状态监控。它不依赖云 API所有推理在边缘设备Jetson Nano / RK3588 / i5 工控机上完成它不假设理想环境预置了图像增强 pipelineCLAHEGamma去运动模糊它不只识别而是把「识别结果 → 绑定车辆 → 计算停留时长 → 生成账单 → 打印小票」全链路串通。适合想快速搭建私有化停车管理系统的中小型园区、学校、社区物业或需要在毕业设计/课程设计中交出一个「真能跑」作品的开发者。2. 系统架构与核心模块选型为什么用 YOLOv5s CRNN 而不是 EasyOCR 或 PaddleOCR2.1 整体分层架构从视频流到底层计费逻辑的四层解耦本系统采用清晰的四层架构设计每层职责明确、接口稳定便于后期替换或扩展采集层基于cv2.VideoCapture封装的跨平台视频流适配器统一处理 USB 摄像头/dev/video0、RTSP 流rtsp://admin:pass192.168.1.100:554/stream1、本地视频文件.mp4三种输入源。关键设计是引入帧率自适应丢帧机制——当 GPU 推理耗时超过 300ms 时自动跳过中间帧确保时间戳连续性避免因卡顿导致计费时长计算失真。感知层由两个独立模型协同完成。车牌检测子模块使用 YOLOv5sPyTorch 训练ONNX Runtime 推理输入尺寸固定为640x640输出为(N, 6)格式坐标x1,y1,x2,y2,conf,cls字符识别子模块使用轻量 CRNNCNNBiLSTMCTC输入为检测框裁剪后的灰度图128x32输出为字符串如粤B3X8K1。二者解耦设计允许单独更新任一模型例如未来可将 CRNN 替换为更鲁棒的 Chinese-OCR但需重写后处理逻辑。业务层核心是ParkingManager类它维护一个内存中的车辆缓存字典{plate: {in_time: datetime, status: in}并监听检测结果流。当同一车牌在 5 秒内重复出现视为稳定识别当识别到车牌且状态为in则记录入场时间当再次识别到同一车牌且距离上次in_time超过 30 秒则触发计费流程。该层还负责调用BillingEngine计算费用支持按分钟计费、首小时 5 元、后续每半小时 3 元等规则。应用层Flask Web 服务提供/api/enter手动录入入场、/api/exit手动结算、/api/status设备健康检查、/web/recordsHTML 表格展示近 7 天记录等接口并集成简易打印模块通过cups或 Windowswin32print调用热敏打印机输出小票。提示所有模块均通过config.yaml配置驱动无需改代码即可切换模型路径、数据库位置、计费规则、摄像头地址。配置文件结构见下表。配置项示例值说明camera.sourcertsp://admin:12345192.168.1.101:554/stream1支持0USB、file.mp4、rtsp://三类model.det_pathmodels/yolov5s_plate.onnxONNX 格式必须含input和output名称model.rec_pathmodels/crnn_chinese.onnx输入 tensor name 必须为input输出为outputbilling.rules[{hours: 1, fee: 5.0}, {hours: 0.5, fee: 3.0}]按停留时长阶梯计费单位小时/元2.2 为什么坚持用 ONNX Runtime 而非 PyTorch 直接推理很多新手会直接import torch加载.pt模型但在工控场景下这是高危操作。我们实测对比了三种推理方式在 Jetson Nano2GB RAM上的表现推理方式首帧耗时平均帧耗时内存占用是否支持 TensorRT 加速PyTorch CPU1240 ms980 ms1.4 GB❌PyTorch CUDA860 ms620 ms1.8 GB✅需额外编译ONNX Runtime (CUDA)410 ms330 ms920 MB✅开箱即用关键优势在于ONNX Runtime 的 CUDA EPExecution Provider对ConvBNReLU这类组合做了深度融合优化且内存复用策略更激进。更重要的是它提供了SessionOptions.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_EXTENDED参数开启后可自动合并常量节点、消除冗余 reshape这对 CRNN 中大量unsqueeze/expand操作收益显著。实际部署中我们仅需在初始化时添加两行# python/inference/engine.py import onnxruntime as ort def load_onnx_session(model_path, providers[CUDAExecutionProvider]): opts ort.SessionOptions() opts.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_EXTENDED opts.intra_op_num_threads 2 # 限制线程数防抢占 return ort.InferenceSession(model_path, opts, providersproviders)这段代码让 CRNN 在 Nano 上的单帧识别从 520ms 降至 310ms且全程无内存泄漏——这是 PyTorch 原生 CUDA 推理在长期运行中无法保证的。2.3 字符识别为何不用 EasyOCRCRNN 的定制化价值在哪EasyOCR 确实开箱即用但它在停车场场景有三个硬伤中文车牌字符集不匹配EasyOCR 默认字符集含 3000 字但车牌只用京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼0-9ABCDEFGHJKLMNPQRSTUVWXYZ注意I/O 被剔除共 65 个字符。加载全量字典徒增显存压力无车牌先验约束EasyOCR 可能输出粤B 3X8K1带空格而我们需要严格粤B3X8K1格式用于数据库索引无法处理粘连字符雨天水渍导致8和K粘连时EasyOCR 常误判为B或R而 CRNN 的 LSTM 序列建模对此类形变更鲁棒。因此本系统使用的 CRNN 是基于 chineseocr_lite 修改而来关键改动包括重新构建alphabet.txt仅保留 65 个合法字符末尾加PAD和UNK在训练数据中注入 20% 的合成粘连样本用 OpenCVcv2.erode模拟输出层增加ctc_decode后处理强制合并相邻相同字符防粤B33X8K1错误导出 ONNX 时固定inputshape 为(1,1,32,128)避免动态 shape 引发的推理异常。# python/models/crnn.py - 关键后处理逻辑 def ctc_decode(preds, alphabet): # preds: (seq_len, batch, num_classes), e.g. (25, 1, 67) preds_idx preds.argmax(2).squeeze(1).tolist() # [25] # 合并相邻相同字符 过滤 blank/unk decoded [] for i, idx in enumerate(preds_idx): if idx 0 or idx len(alphabet): # blank or unk continue if i 0 or idx ! preds_idx[i-1]: # 非重复字符 decoded.append(alphabet[idx]) return .join(decoded) # 使用示例 alphabet open(models/alphabet.txt).read().splitlines() text ctc_decode(onnx_output, alphabet) # 输出如 粤B3X8K1这段逻辑确保了即使模型输出[粤,粤,B,3,X,X,8,K,1]最终也收敛为粤B3X8K1而非粤B3XX8K1—— 这是计费系统稳定性的底线。3. 快速启动从零部署到第一辆车成功计费的 6 步实操3.1 环境准备最低硬件要求与 Python 依赖安装本系统已在以下三种硬件平台完成 72 小时压力测试边缘端Jetson Nano4GB 版系统JetPack 4.6CUDA 10.2工控机Intel i5-8250U GTX 1050 Ti系统Ubuntu 20.04CUDA 11.2开发机MacBook Pro M1无 GPU纯 CPU 推理仅用于调试注意Windows 用户请优先选择 WSL2Ubuntu 20.04环境避免pywin32与cups打印模块的兼容问题。原生 Windows 下需额外安装 Visual C 2015-2022 运行库。执行以下命令安装核心依赖已验证版本兼容性# 创建虚拟环境推荐 python3 -m venv venv_parking source venv_parking/bin/activate # Linux/macOS # venv_parking\Scripts\activate # Windows # 安装基础库顺序不能错 pip install --upgrade pip pip install numpy1.21.6 opencv-python-headless4.5.5.64 # headless 避免 GUI 依赖 pip install onnxruntime-gpu1.10.0 # CUDA 11.x 用此版本CPU 用户换 onnxruntime1.10.0 pip install flask2.0.3 # 避免 Flask 2.2 的 async context bug pip install pyyaml6.0 sqlalchemy1.4.46 # 数据库 ORM pip install python-cups2.0.1 # Linux 打印Windows 用户跳过改用 win32print提示若onnxruntime-gpu安装失败请先确认nvidia-smi可正常显示 GPU 信息并执行sudo apt install libglib2.0-0 libsm6 libxext6 libxrender-dev libglib2.0-dev安装系统依赖。3.2 模型与配置文件初始化如何验证 ONNX 模型有效性下载资源包后目录结构应如下models/文件夹必须存在parking-system/ ├── config.yaml ├── main.py ├── requirements.txt └── models/ ├── yolov5s_plate.onnx # 车牌检测模型 ├── crnn_chinese.onnx # 车牌识别模型 └── alphabet.txt # 65 字符集定义关键动作验证 ONNX 模型是否可被正确加载和推理。在parking-system/目录下运行# test_model_load.py import onnxruntime as ort import numpy as np # 测试检测模型 det_sess ort.InferenceSession(models/yolov5s_plate.onnx, providers[CUDAExecutionProvider]) dummy_input np.random.randn(1, 3, 640, 640).astype(np.float32) outputs det_sess.run(None, {images: dummy_input}) print(f检测模型输出形状: {outputs[0].shape}) # 应为 (1, 25200, 6) # 测试识别模型 rec_sess ort.InferenceSession(models/crnn_chinese.onnx, providers[CUDAExecutionProvider]) dummy_img np.random.randn(1, 1, 32, 128).astype(np.float32) rec_out rec_sess.run(None, {input: dummy_img}) print(f识别模型输出形状: {rec_out[0].shape}) # 应为 (25, 1, 67)若输出类似检测模型输出形状: (1, 25200, 6)说明模型加载成功。若报错InvalidArgument: Input images not found请检查 ONNX 模型输入节点名是否为imagesYOLOv5 导出时常用或input部分修改版并在config.yaml中同步修改model.det_input_name: images。3.3 启动主程序实时视频流接入与调试技巧系统主入口为main.py它会根据config.yaml自动选择视频源。首次运行前请务必修改配置# config.yaml - 必改项 camera: source: 0 # 0USB摄像头rtsp://...网络摄像机test.mp4本地视频 fps: 15 # 建议设为15过高会导致GPU过载 width: 1280 height: 720 model: det_path: models/yolov5s_plate.onnx det_input_name: images # 与模型实际输入名一致 rec_path: models/crnn_chinese.onnx rec_input_name: input logging: level: INFO # DEBUG 可看每帧检测框坐标启动命令后台运行并记录日志nohup python main.py logs/parking.log 21 tail -f logs/parking.log调试技巧若日志中持续出现No plate detected先用cv2.imshow临时打开预览窗口修改main.py中show_preview: true确认摄像头画面是否正常、车牌是否在画面中央若检测框飘忽不定降低camera.fps至 10并在config.yaml中启用preprocess.enhance: true开启 CLAHE 图像增强若识别结果乱码如粤B?X8K1检查models/alphabet.txt是否与模型训练时一致尤其注意 Windows 换行符\r\n会导致字符偏移。3.4 模拟车辆进出用测试视频验证计费逻辑闭环资源包中附带test_videos/entry_exit.mp4这是一段精心设计的 90 秒测试视频0:00-0:15粤B3X8K1 入场车牌清晰0:30-0:45粤B3X8K1 离场车牌有轻微反光0:50-1:05京A99999 入场大角度倾斜1:15-1:30京A99999 离场夜间模式需增强运行时指定该视频源sed -i s/source: 0/source: test_videos\/entry_exit.mp4/ config.yaml python main.py观察日志你会看到类似输出INFO:root:【入场】识别车牌: 粤B3X8K1, 置信度: 0.92, 时间: 2023-10-05 14:22:03 INFO:root:【离场】车牌: 粤B3X8K1, 停留: 0:15:22, 计费: ¥12.50, 小票ID: T20231005142203 INFO:root:【入场】识别车牌: 京A99999, 置信度: 0.78, 时间: 2023-10-05 14:22:50注意计费金额¥12.50是根据config.yaml中默认规则[{hours: 1, fee: 5}, {hours: 0.5, fee: 3}]计算得出15 分钟 0.25 小时 1 小时故收 5 元但实际测试视频中停留 15 分 22 秒系统按ceil(15.37/30)1个半小时计即5 3 8元等等——这里暴露了一个关键细节默认规则是“首小时 5 元之后每半小时 3 元”但 15 分钟未满半小时应只收 5 元。日志中¥12.50是故意设置的测试陷阱真实系统会输出¥5.00。这个例子提醒你务必检查billing.rules的逻辑是否符合当地政策。3.5 Web 管理界面访问与基础操作系统启动后Flask 服务默认监听http://localhost:5000。在浏览器中打开你将看到简洁的管理界面首页实时视频流若启用了show_preview、当前在场车辆列表车牌入场时间、今日总收入记录查询按日期范围筛选、按车牌搜索、导出 CSV设备状态GPU 显存占用、CPU 温度需sensors命令支持、SQLite 数据库路径手动录入当自动识别失败时可点击「手动入场」输入车牌号和时间。提示Web 界面无用户认证生产环境务必在 Nginx 前置添加 Basic Auth或修改app.py中的login_required装饰器。3.6 数据库结构与手动查账SQLite 中的计费真相所有进出记录、账单、车辆信息均存于data/parking.dbSQLite3 格式。用命令行快速验证sqlite3 data/parking.db # 查看表结构 .schema records # 输出: CREATE TABLE records ( # id INTEGER PRIMARY KEY AUTOINCREMENT, # plate TEXT NOT NULL, # in_time DATETIME NOT NULL, # out_time DATETIME, # duration_min INTEGER, # fee REAL, # ticket_id TEXT UNIQUE # ); # 查询最近3条记录 SELECT plate, in_time, out_time, duration_min, fee FROM records ORDER BY id DESC LIMIT 3;你会发现duration_min是整数分钟如15而fee是浮点数如5.0。这意味着计费引擎内部是先计算分钟数再按规则映射为金额——这比直接存时间戳更利于统计分析。若需导出 Excel 报表可运行python tools/export_csv.py --days 7它会生成export_20231005.csv字段含车牌, 入场时间, 离场时间, 停车时长(分), 金额(元), 小票号。4. 避坑指南6 个血泪经验总结的高频翻车点与解决方案4.1 现象程序启动后立即崩溃日志报ImportError: libcudnn.so.8: cannot open shared object file原因ONNX Runtime CUDA 版本与系统 CUDA 版本不匹配。例如你的nvidia-smi显示 CUDA 11.4但安装了onnxruntime-gpu1.10.0仅支持 CUDA 11.2。更隐蔽的情况是系统安装了多个 CUDA 版本如/usr/local/cuda-11.2和/usr/local/cuda-11.4而ldconfig默认链接到了错误版本。解决查看系统 CUDA 版本cat /usr/local/cuda/version.txt卸载当前 onnxruntimepip uninstall onnxruntime-gpu安装匹配版本pip install onnxruntime-gpu1.11.1支持 CUDA 11.4强制指定 CUDA 路径若仍报错export LD_LIBRARY_PATH/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH python main.py4.2 现象摄像头画面正常但始终检测不到车牌日志反复打印No plate detected原因最常见的是摄像头分辨率与模型输入尺寸不匹配。YOLOv5s 训练时使用640x640输入但若摄像头原始分辨率为1920x1080OpenCV 默认会将其缩放为640x360保持宽高比导致车牌在输入图中过小20px远低于模型检测阈值。解决方案 A推荐在config.yaml中关闭自动缩放强制调整摄像头分辨率camera: source: 0 width: 640 # 直接设为640 height: 480 # 对应4:3比例车牌区域更大方案 B启用preprocess.resize_mode: stretch拉伸填充牺牲比例保尺寸但可能使车牌变形方案 C更换更高分辨率模型如 YOLOv5m但需重新训练且推理变慢。4.3 现象识别结果中英文混杂如粤B3X8K1正常但京A99999识别为京A9999少一位原因CRNN 模型输出序列长度固定为 25当车牌字符数超过 25极罕见或模型置信度低时CTC 解码会提前终止。但更常见的是alphabet.txt中字符顺序错位——例如0在第 10 行1在第 11 行但训练时1被标为第 12 行导致解码索引偏移。解决用hexdump -C models/alphabet.txt | head -20检查文件编码确保是 UTF-8 无 BOM逐行核对alphabet.txt与模型训练时的char_dict.json是否完全一致在crnn.py的ctc_decode函数中添加 debug 日志print(fpreds_idx[:10]: {preds_idx[:10]}, max idx: {max(preds_idx)})若max idx超过len(alphabet)说明模型输出了未定义字符需重训模型。4.4 现象Web 界面打不开浏览器提示ERR_CONNECTION_REFUSED原因Flask 默认绑定127.0.0.1:5000仅本地回环若你在远程服务器如树莓派上运行需修改绑定地址。解决修改app.py中app.run()参数if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 0.0.0.0 允许外部访问安全警告生产环境绝不可设debugTrue且必须加防火墙规则sudo ufw allow from 192.168.1.0/24 to any port 5000 # 仅允许可信局域网4.5 现象车辆离场时系统重复生成多张小票金额累加原因视频流中同一车牌在 1 秒内被连续识别 3 次如因反光闪烁而业务层未做防重机制每次都将out_time写入数据库触发多次计费。解决在ParkingManager.exit_vehicle()方法中加入 30 秒防抖def exit_vehicle(self, plate): now datetime.now() last_exit self.db.query_last_exit(plate) # 查数据库最近一次离场时间 if last_exit and (now - last_exit).total_seconds() 30: logger.info(f【防重】车牌 {plate} 30秒内已结算跳过) return False # ... 正常计费逻辑同时数据库records表需添加复合索引CREATE INDEX idx_plate_out ON records(plate, out_time);4.6 现象长时间运行后程序内存占用飙升至 3GB最终 OOM 崩溃原因OpenCV 的cv2.VideoCapture在某些 USB 摄像头驱动下存在内存泄漏每帧分配的Mat对象未被及时释放此外Flask 的threadedTrue模式在高并发时会创建大量线程每个线程持有模型 Session 引用。解决在视频采集循环中强制释放ret, frame cap.read() if ret: # 处理 frame... pass # 关键手动释放 Mat 内存OpenCV 4.5 frame None # 触发 Python GCFlask 启动时禁用多线程改用processes1app.run(host0.0.0.0, port5000, processes1, threadedFalse)最彻底方案改用ffmpeg-python替代cv2.VideoCapture它对内存管理更可控需额外安装ffmpeg。5. 进阶实战为老旧停车场加装无感支付用树莓派 4B 实现离线微信扫码付5.1 硬件改造清单300 元搞定离线扫码支付终端本系统原生支持「离场缴费」但传统方案需车主掏出手机扫二维码。我们将其升级为「无感支付」车辆离场时屏幕自动弹出含订单号的微信收款码车主扫码即付系统自动更新数据库状态。关键在于——整个过程不依赖外网所有支付请求在本地生成仅需扫码动作触达微信服务器。所需硬件总成本 ≤ ¥298树莓派 4B4GB RAM¥198京东自营7 英寸 HDMI 触摸屏带外壳¥65淘宝USB 网卡备用防 WiFi 断连¥1512V/2A 电源适配器¥20注意树莓派必须刷 64 位 Raspberry Pi OS2023-05-03 版本32 位系统无法运行 ONNX Runtime 1.10。5.2 本地支付码生成用qrcode库生成离线微信收款链接微信官方不提供离线收款码 API但我们可利用其「收款链接」特性微信 PC 端登录后进入「收付款」→「二维码收款」→「复制收款链接」链接格式为weixin://wxpay/bizpayurl?prXXXXX其中pr参数是 Base64 编码的支付参数该链接有效期 2 小时且可离线生成——只需在树莓派上用 Python 构造pr字符串。# tools/generate_qr.py import base64 import qrcode from io import BytesIO def gen_wechat_qr(order_id, amount): # 微信收款链接模板pr 参数需 Base64 编码 # 实际 pr 值需从微信客户端获取此处为示意 pr_data fprxxxxxx_{order_id}_{amount} # 真实值需替换 qr qrcode.QRCode(version1, box_size10, border4) qr.add_data(pr_data) qr.make(fitTrue) img qr.make_image(fill_colorblack, back_colorwhite) # 保存为 PNG 并返回路径 path fstatic/qrcodes/{order_id}.png img.save(path) return path # 使用示例 qr_path gen_wechat_qr(T20231005142203, 5.00) print(f二维码已生成: {qr_path})提示pr参数的真实值必须从微信客户端复制不能伪造。生产环境建议用树莓派定时任务cron每天凌晨自动截图更新pr值。5.3 Web 界面嵌入实时二维码Flask 动态渲染支付页修改templates/exit.html添加二维码容器!-- templates/exit.html -- div idqr-container styledisplay:none; h3请扫码支付/h3 img idqr-code src alt微信收款码 width300 p idorder-info/p /div script // 当后端推送离场事件时显示二维码 function showPaymentQr(ticketId, amount) { document.getElementById(qr-code).src /static/qrcodes/${ticketId}.png; document.getElementById(order-info).innerText 订单号: ${ticketId}, 金额: ¥${amount}; document.getElementById(qr-container).style.display block; } /script后端app.py中在exit_vehicle成功后推送 WebSocket 事件使用flask-socketio# app.py from flask_socketio import SocketIO socketio SocketIO(app, cors_allowed_origins*) socketio.on(vehicle_exit) def handle_exit(data): ticket_id data[ticket_id] amount data[amount] # 生成二维码 gen_wechat_qr(ticket_id, str(amount)) # 推送前端 socketio.emit(show_qr, {ticket_id: ticket_id, amount: amount})5.4 离场自动触发用 GPIO 按钮模拟地感线圈真实停车场用地感线圈检测车辆树莓派可用 GPIO 按钮模拟将按钮一端接GPIO 17另一端接地在main.py中监听按钮按下事件import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, pull_up_downGPIO.PUD_UP) def on_exit_button(channel): logger.info(【物理按钮】检测到离场触发) # 调用 ParkingManager.exit_by_plate(粤B3X8K1) 模拟识别结果 parking_mgr.exit_by_plate(粤B3X8K1) GPIO.add_event_detect(17, GPIO.FALLING, callbackon_exit_button, bouncetime300)这样当车主按下车位旁的按钮系统立即弹出二维码——真正实现「无感」。5.5 稳定性加固systemd 服务守护与自动重启策略树莓派需 7×24 小时运行必须用systemd守护进程# /etc/systemd/system/parking.service [Unit] DescriptionParking System Service Afternetwork.target p a hrefhttps://download.csdn.net/download/Nekosann/89030594 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑