尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
YOLOv11农业病害识别实战:从数据准备到部署全流程
1. 项目思路拆解为什么拿YOLOv11做农业病害识别1.1 病害识别的本质不是分类是定位农业病害识别这个需求我刚接触时也有人拿图像分类模型直接怼。比如弄一个 ResNet把一张叶子照片丢进去告诉你是“有锈病”还是“没病”。听起来很合理实际一用就发现不对劲。田间地头拍回来的照片不是干净的单片叶子经常是一株番茄上既有健康叶又有发黄、带斑点的病叶甚至一株上面同时混着两种病害。这种情况用分类模型只能回答“这张图更像哪一种病”完全回答不了“病斑在哪”“面积多大”“是否已经扩散到茎秆”。所以农业病害识别的真实需求是“找出病变区域”这是典型的目标检测任务。目标检测模型做的事是在图上画框每个框标出类别和位置。这正好匹配农技巡检的实际场景——拍一张图模型把有问题的区域框出来农户或者后端系统再决定要不要打药。YOLO 系列又是目标检测里落地成本最低的一类单阶段检测一次前向就能出框速度上普遍比两阶段检测器快一个身位很适合做成田间实时巡检工具。1.2 YOLOv11 的几个优势与适用边界YOLOv11 是 Ultralytics 在 YOLOv8 基础上继续迭代的一版核心结构做了调整骨干网络里加入了一些更轻量的模块整体在相同参数量级上比前代更稳。我选它不是因为“新版一定更好”这种跟风心理而是因为三点第一它对小目标和大目标都做了取舍优化农业病害这种“目标尺寸参差不齐”的场景比较契合第二Ultralytics 生态把训练、验证、导出、部署的链路全打通了一个配置文件就能从训练跑到导出不用自己拼一堆脚本第三预训练权重可以直接继承迁移学习起步快。但也要泼盆冷水YOLOv11 不是万能药。病害识别最难的不是模型结构而是数据。如果标注错、类别混、样本极端不均衡任何 SOTA 模型都救不回来。这篇实战的大头其实是在数据准备和训练调参上模型本身反而最省事。1.3 技术路线与依赖组件总览整个项目走下来大概是这个链路数据集准备 → 格式整理 → 标注校验 → 写数据配置文件 → 选模型规格 → 训练 → 看指标 → 调参 → 验证推理 → 导出部署。我用的数据集是公开的 PlantDoc再补充了一部分自定义的田间照片。训练环境是一台 8GB 显存的本地 GPU 机器如果你手头没有独显云端 GPU 环境也能跑思路完全一样。这套流程说实话并不复杂难的是每一步都有人踩坑。比如标注坐标越界导致训练直接报错、数据集类别名写错导致 mAP 虚高、小病斑漏检怎么调都提升不上去。我后面会把这些坑挨个拆开讲。2. 数据集准备PlantDoc 与自定义数据混合处理2.1 PlantDoc 数据集特点与局限PlantDoc 是一个公开的植物病害图像数据集覆盖番茄、玉米、土豆、葡萄等常见作物的叶片状态总共大约 2600 张图片类别有 13 个左右包括健康叶片和各类病害。它的优点是类别贴近真实农业场景、图片来源是自然拍摄而不是实验室棚拍训练出来的模型更容易迁移到农田环境。但它的局限也很明显第一图片数量不算多有的类别可能只有几十张存在明显的类别不均衡第二PlantDoc 发布时最初以图像分类形式组织用目标检测视角去看很多图片里有多片叶子、多个病斑但原始标注未必提供逐框信息需要你自己准备框级别的标签第三部分图片分辨率不高、光照条件差直接拿来训练会影响模型对纹理细节的敏感度。因此拿到手第一件事不是急着训练而是先做一轮“清洗”。2.2 数据清洗脚本与类别统计数据清洗这件事听起来不起眼但它是决定 mAP 天花板的关键环节。我建议下载解压后先跑一个统计脚本看看图片和标签的对应关系、类别分布到底什么样。import os from pathlib import Path from collections import Counter img_dir Path(datasets/plantdoc/images) label_dir Path(datasets/plantdoc/labels) suffixes {.jpg, .jpeg, .png, .bmp} images [p for p in img_dir.rglob(*) if p.suffix.lower() in suffixes] print(图片数量:, len(images)) counter Counter() missing_label [] for img_path in images: label_path label_dir / (img_path.stem .txt) if not label_path.exists(): missing_label.append(img_path.name) continue lines label_path.read_text(encodingutf-8).strip().splitlines() if not lines: missing_label.append(img_path.name) for line in lines: parts line.strip().split() if len(parts) 5: counter[int(parts[0])] 1 # 这里可以做更多异常检查比如坐标越界 print(类别ID统计:, counter) print(图片缺失标签/空标签数量:, len(missing_label))我跑完统计后干了几件事把模糊到人眼都分不清病害类型的图直接删掉把某些类别里重复度极高的近相似图压缩数量把明显标错的类改掉。很多公开数据集都存在“标签噪声”早期花一两个小时做一次清洗后面训练会顺利很多。2.3 标注制作与 YOLO 格式转换PlantDoc 原始数据如果不是框级别标注就需要你自己补框。我的做法是先找一个开源的标注工具把图片加载进去为每张图里的病害区域画矩形框导出成 YOLO 格式。YOLO 格式的标注文件是每个图像对应一个 txt每一行代表一个目标框类别ID cx cy w h其中 cx 和 cy 是框中心点的坐标w 和 h 是框的宽高四个数值都用图片宽高做了归一化范围在 0 到 1 之间。举个例子一张 640x480 的图上某个病斑框左上角在 (160, 120)右下角在 (320, 300)那框宽就是 160高是 180中心点是 (240, 210)归一化后标注大概就是2 0.375000 0.437500 0.250000 0.375000这一步最费时间但没有捷径。偶尔会用预训练目标检测模型先跑一遍生成伪标签再人工修正能省一些鼠标点击但小病斑这种难目标还是得靠人眼确认。标注的原则是“宁缺毋滥”标不准的框比不标更坏因为模型学到的是错误位置。2.4 数据划分与目录结构整理数据准备好后我按 8:2 的比例划分训练集和验证集。目录结构按 Ultralytics 的默认习惯组织datasets/ └── plantdoc/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml划分时要注意同一张原始图不能既出现在训练集又出现在验证集尤其是做数据增强时同一来源的图会被复制多份必须在划分之前处理完。我当时就用一个随机采样脚本按图片文件名列表做分配而不是直接挪文件避免遗漏。另外验证集一定要保留真实分布不要刻意只挑清晰的图。验证集图片质量越高训练后的 mAP 越好看但那是骗自己。我在实践中尝过这个教训——训练时 mAP 到 0.88下地实测直接拉胯就是因为验证集跟田野实拍差距太大。3. 环境配置与 YOLOv11 选型解读3.1 硬件门槛与依赖安装先说说硬件很多新手怕门槛太高其实 YOLOv11 对硬件要求不算离谱。你用 CPU 也能完成训练但速度感人几百张图都要跑几个小时。如果有一块 8GB 左右的 NVIDIA 显卡训练 13 个类别的病害数据已经足够了。如果连独显都没有云端免费 GPU 环境和在线 Notebook 是更现实的选择直接上传代码和数据就能跑。依赖安装非常简单核心就是装一个包含训练推理能力的深度学习环境再装 PyTorch 和 Ultralytics 包pip install ultralyticsUltralytics 包会把命令行工具 yolo 一起装好它会根据网络情况自动下载 yolo11n.pt 这类预训练权重。装的时候如果遇到网络慢可以手动先把预训练权重下载到本地再通过 model 参数指过去。版本方面我建议选稳定版不要追最新开发版曾经试过开发版某次更新把所有 checkpoint 解析方式改了旧权重直接读不了很坑。3.2 模型规格怎么选YOLOv11 提供了 n、s、m、l、x 几个规格对应从轻量到重量的五种配置。我用一个表格把它们的关键差异摆一下你不需要记参数看相对关系就行。规格参数量相同输入下速度显存占用趋势适用场景n较小最快最低嵌入式设备、大批量实时推理s中等快较低常规 GPU、农业巡检主力规格m中上中等中上精度优先且 GPU 显存 8GB 以上l/x大慢高离线高精度场景农业病害识别的目标不算特别小但叶片上的病斑形态差异大太轻量的模型容易在纹理细节上犯糊涂。我最终选了 yolo11s 作为主力规格理由很简单它在这个数据规模下训练快、显存占用可控推理速度也够。你不是在做高精度论文实验而是在做能跑起来的工程s 级是性价比最高的档位。想追求更高精度可以先跑通 s 级全流程再换 m 级做对比。3.3 训练超参数背后的逻辑Ultralytics 的训练配置是 yaml 加命令行参数混合控制的新手最容易忽略的就是 batch、epochs、imgsz 这三个参数背后的联动关系。imgsz 是输入分辨率默认 640。分辨率越高模型能看到更多细节但显存占用按平方级别上涨。我的经验是如果你的数据里病斑目标相对大640 足够如果病斑小、定位要求高就上 960。这个参数不能无脑调高要在显存允许的范围内做增量实验。batch 是单次喂入模型的图片数量。显存不够时就减 batch这是最直接的止损手段。但 batch 太小会让梯度噪声变大训练不稳。一般 8GB 显存跑 yolo11s、imgsz640 时batch 开 16 左右比较合适显存不够就降到 8。epochs 不要一开始就设两三百推荐从 100 开始。Ultralytics 有早停机制训练中验证集指标不再提升到一定次数就会自动停。我先用 60 个 epoch 跑通流程看曲线走势再决定要不要加长训练周期。单纯堆 epochs 不会让模型质变数据质量才是主要矛盾。4. 训练全流程从配置到结果解读4.1 写好的数据集 YAML 文件Ultralytics 训练的第一步是告诉模型“我的数据在哪、有几类、类名是什么”。打开任意文本编辑器创建一个 data.yaml内容结构大致如下# 训练和验证图片的绝对路径也可以写相对路径 path: /home/user/datasets/plantdoc train: images/train val: images/val # 类别数量与类别名顺序必须和标注文件里的 class_id 一致 nc: 13 names: 0: tomato_healthy 1: tomato_bacterial_spot 2: tomato_early_blight 3: tomato_late_blight 4: tomato_leaf_mold 5: tomato_leaf_curl_virus 6: corn_healthy 7: corn_rust 8: corn_gray_leaf_spot # ... 其余类别按实际情况写这里最容易翻车的点就是 names 顺序。YOLO 标注文件里的第一个数字是类别 ID这个 ID 必须和 names 的索引一一对应。我曾经把排序弄错结果训练出来把锈病当成了叶斑病而且 mAP 还挺高不细看完全发现不了所以写完 yaml 后务必抽三五个样本人工对一下标注和类别。路径建议写绝对路径。相对路径在本地一次跑通没毛病但项目换机器、换目录结构后会非常痛苦。我在团队协作时吃过这个亏队友的路径层级和我不一样数据配置文件改了三次才好。如果项目打算长期维护写个统一的环境变量或固定目录会比较省心。4.2 训练命令与实时日志解读环境就绪、数据整理无误后就可以启动训练了。我经常用的命令是这样cd /home/user/plantdoc-project yolo detect train \ datadata.yaml \ modelyolo11s.pt \ epochs120 \ imgsz640 \ batch16 \ workers4 \ device0 \ projectruns/plantdoc \ nameexp_s_640model 参数传的是预训练权重 yolo11s.pt第一次运行会自动下载。下面几个参数我展开说一下device0 表示使用第一块 GPU如果是 CPU 环境就改成 devicecpuproject 和 name 共同决定每轮实验的输出目录建议每次实验起一个能看懂的名字后面比较模型时不用猜workers 是数据加载进程数Windows 下如果经常崩就把 workers 调成 0amp 默认开着这是混合精度训练能省显存、加速训练保持默认即可。训练开始后终端会实时刷新一列字段Epoch、GPU_mem、Box(P)、R、mAP50、mAP50-95、loss 等。很多新手只盯 loss其实 loss 下降和检测指标之间不是线性关系。有时候 box_loss 掉了但 mAP 不动可能是模型在小目标上的召回出了问题。看指标应该以 mAP50 和 mAP50-95 为主loss 为辅。4.3 训练曲线与调参信号训练结束后output 目录里会生成 results.png把每个 epoch 的损失曲线和指标曲线画在一起。读这张图的方式很简单训练 loss 和验证 loss 一起下降说明正常验证 loss 先降后涨、训练 loss 还在降那就过拟合了需要做的是减少 epochs 或增加数据增强而不是继续训下去。我印象最深的一次模型训练到第 80 轮时 mAP50 已经 0.93但在真实田间的几张测试图上很多细小的早期病斑完全没框出来。回去看了一眼数据发现训练集里的框普遍偏大标注时喜欢把整片病区圈起来忽略了零散的小病斑模型自然学成了“只盯大片区域”。这种问题调参没用必须回到标注环节去补小目标框。4.4 用验证集做推理测试训练完成后先用验证集跑一次推理肉眼检查结果yolo predict \ modelruns/plantdoc/exp_s_640/weights/best.pt \ sourcedatasets/plantdoc/images/val \ conf0.25 \ saveTrueconf 是置信度阈值低于这个值的框会被过滤掉。验证集推理可视化图会保存到 runs/plantdoc/exp_s_640/predict 里你需要一张一张翻重点看两类错误该框的没框漏检和框到背景或其他物体上误检。漏检多就说明目标定位能力不足优先查标注小目标是否足够误检多就说明类别区分不够优先查是否有类别标注错乱。5. 农业病害场景的典型问题与避坑实录5.1 样本类别不平衡PlantDoc 这类公开数据集最典型的问题就是类别不均衡有的类别几十张有的类别几百张。YOLOv11 默认按均匀概率采样所有样本少数类被模型看到的次数就少最终表现天然偏向多数类。解决思路我按实践顺序排了一下优先级从高到低首先尝试在框架内部解决。Ultralytics 训练时会在数据采样和增强层面做不少功夫你可以在数据准备阶段把多数类的冗余图删掉一部分让各类样本量不要悬殊到十倍以上。其次对少数类做离线增强。离线增强不是简单复制图片而是要生成有区分度的变体旋转 15 度、上下翻转、改变亮度对比度、加一点高斯噪声相当于人工扩充了少数类的特征空间。最后如果条件允许补采真实样本。少数类只有二三十张图的时候任何增强手段都很难让模型学到真正稳的特征真实数据永远比合成数据好。我跑项目时发现玉米锈病只有不到 40 张图而番茄健康叶有 300 多张。我把番茄健康叶删到 200 张左右再对玉米锈病做了多组离线增强把训练集扩到 120 张。最终玉米锈病的 mAP50 从 0.71 涨到了 0.86效果立竿见影。5.2 小目标漏检问题农业病害里真正的痛点是早期病斑刚出现时往往只有几个像素大小的变色点。YOLOv11 的检测头对中小目标的支持不错但面对极端小目标还是吃力。这个问题我用两种方法缓解。第一种是把 imgsz 从 640 提高到 960。输入分辨率变大后小目标在特征图上的有效像素变多模型更容易学到它的纹理和边缘特征。代价是训练速度明显下降显存占用也会涨一截。第二种是切片推理也就是把原始大图切成几块小图分别检测再合并结果。这个方法我用在最终部署阶段因为田间拍摄的叶片大图往往分辨率很高直接缩放会导致病斑细节丢失切片后再检测反而更准。需要提醒的是如果你训练时用 640 推理时用 960性能会受影响。训练和推理的输入尺寸尽量保持一致或者至少在同一量级。5.3 显存溢出与训练中断训练中途报 CUDA out of memory 是很常见的事我就遇到过多次。第一次报错是在我试 m 级模型、imgsz 开到 960、batch 还想保持 16 的时候。解法排优先级先把 batch 从 16 降到 8还不行就降到 4再不行把 imgsz 从 960 降到 736最后就换更小的模型规格。注意别一上来就换模型因为你可能不需要那么大的容量纯属超参数设置问题。另一个被忽略的坑是同一张卡上其他任务占显存。我跑过最无语的一次是训练刚开始就 OOM查了半天发现是后台还挂着之前的推理进程。训练前先敲一下 nvidia-smi 看当前显存占用省得白白浪费时间。5.4 标注文件里的隐性炸弹YOLO 训练有时候报奇怪的错误比如 index out of range、ValueError其实问题往往出在标注文件上。常见情况有类别 ID 超过了 yaml 里 nc 的值某个坐标算出来大于 1 或者小于 0txt 文件内容为空文件编码不是 UTF-8。我建议写一个小脚本在训练前把所有 txt 扫一遍发现异常就报警。我之前的统计脚本已经留了接口可以扩展检查坐标范围for label_file in label_dir.rglob(*.txt): for i, line in enumerate(label_file.read_text(encodingutf-8).splitlines()): parts line.strip().split() if len(parts) ! 5: print(f异常: {label_file} 第{i1}行字段数不对: {line}) continue cls_id, cx, cy, w, h map(float, parts) if cls_id 13: print(f异常: {label_file} 类别ID越界: {cls_id}) if not (0 cx 1 and 0 cy 1 and w 0 and h 0): print(f异常: {label_file} 坐标异常: {line})检查脚本和清洗脚本建议写进项目里每次换数据源、合并新标注时都跑一遍。数据源一变标注格式的坑一定会重现这条经验值很多次反复验证过。5.5 泛化性不足从实验室到田间训练集里照片大多是人在植株旁边拍的光线、角度都相对统一。真实上岗时机器可能装在巡检车上或者农户上午逆光拍、傍晚暗光拍甚至有水渍、反射和土块遮挡模型很容易原形毕露。解决泛化问题我的经验是双管齐下一是在训练时把增强参数拉高一点让模型见过更多光照变化Ultralytics 默认开了 hsv 增强但你可以在配置文件里把 hsv_h、hsv_s、hsv_v 调高二是收集真实场景的“负样本”包括没有病害的健康场景、手部遮挡、远处全景图让模型学会在这些情况下不产生误检。目标检测模型很容易把“病斑”和“阴影”搞混增加负样本是最靠谱的矫正方式。6. 部署推理与后续落地方向6.1 本地批量推理脚本训练得到 best.pt 权重后除了命令行推理很多时候需要在自己的程序里调用模型做批量处理。我习惯写一个简短的 Python 脚本from ultralytics import YOLO model YOLO(runs/plantdoc/exp_s_640/weights/best.pt) results model.predict( sourcetest_imgs/, conf0.25, iou0.5, saveTrue, save_txtTrue, verboseFalse ) for result in results: box result.boxes # 包含坐标、置信度、类别ID names result.names for i in range(len(box.cls)): class_id int(box.cls[i].item()) conf float(box.conf[i].item()) x1, y1, x2, y2 box.xyxy[i].tolist() print(f{names[class_id]} 置信度 {conf:.2f} 框坐标 ({x1:.1f}, {y1:.1f}, {x2:.1f}, {y2:.1f}))注意 result.boxes.xyxy 里的坐标是原始图像像素坐标不是归一化坐标。如果你要做病斑面积估算记得按像素面积乘上标定系数换算不要直接在归一化坐标上算这个细节最容易出错。6.2 模型导出与轻量化部署到嵌入式设备或移动端时PyTorch 权重太重不适合直接跑推理。Ultralytics 提供了导出接口非常方便yolo export modelbest.pt formatonnx # 导出通用 ONNX yolo export modelbest.pt formatopenvino # 导出 OpenVINO 中间表示 yolo export modelbest.pt formattflite # 导出 TensorFlow Lite实际项目里如果部署到单板计算机我会导出 OpenVINO 格式推理速度比 PyTorch 快不少如果部署到手机端优先试试 TensorFlow Lite。导出 OPTION 里有个半精度量化开关可以进一步缩小体量、加快速度但 mAP 会掉 1 到 3 个百分点。要不要量化取决于你的目标设备算力是否吃紧。我自己的建议是导出前先在本地把 ONNX 和 OpenVINO 两种格式的推理结果跟 PyTorch 原版对比一下确认检测框坐标没有发生偏移。框架转换时偶尔会出现算子兼容问题不校验就直接上设备现场会很难调试。6.3 后续扩展想法病害检测模型做出来以后可以扩的方向其实不少。我当时做完基础识别后接着做了两件事第一是给模型加了一个置信度过滤和面积统计的后处理模块让农户端小程序直接输出“检测到番茄早疫病病斑占比 8%”这种结论而不是甩一张画框的图第二是开始收集误检样本每天把用户上报的错误结果拉回来定期增量训练。增量训练不需要从零开始用原来 best.pt 当预训练权重在新数据上只跑几十个 epoch 就够了。这个闭环跑起来之后模型在真实场景的表现会慢慢变好比任何一次性训练都有用。农业这个场景数据分布很难一劳永逸换一个种植季节、换一个品种病害形态就可能不一样所以项目能不能长期用下去很大程度上取决于你有没有把这套增量迭代的机制搭好。这里还有一个我后来才悟到的教训模型效果好的时候一定记得把数据集、标注文件、训练参数和权重一起归档保存。我有个习惯每个实验跑完会把 data.yaml 和训练命令复制到项目说明文件里。看起来像是在做记录但三个月后你要把效果复现出来的时候这堆记录就是唯一救命的线索。我当时有个模型效果不错后面做增量训练时不小心覆盖了原来的权重文件又找不到当时的配置只能重新训练凭空多耗了两天时间。你不想走这个弯路的话就从现在开始养成好习惯吧。
RELATED

相关推荐

九宫格与方块轮盘抽奖H5实现:核心逻辑、性能优化与避坑指南

九宫格与方块轮盘抽奖H5实现:核心逻辑、性能优化与避坑指南

九宫格抽奖和方块轮盘抽奖,这两个东西听起来像是运营活动页面的“标配”,但真正动手写的时候,你会发现坑比想象中多得多。我做过不下十个类似的抽奖H5,从最早的纯CSS动画硬怼,到后来用Canvas重写,再到现在的…

📅 2026/10/17 12:44:41
AI接入飞书机器人:基于OpenClaw的完整实践与排错指南

AI接入飞书机器人:基于OpenClaw的完整实践与排错指南

先说结论:把 AI 接进飞书这件事,难点从来不在模型本身,而在于如何把一个跑在终端里的原型,变成一个同事真的愿意在群里 的常驻机器人。我上个月用 OpenClaw 做了一个完整的飞书机器人接入,现在它在好几个测试群里稳定…

📅 2026/10/17 12:39:40
用Agent Skills构建多平台订单自动化工作流实战解析

用Agent Skills构建多平台订单自动化工作流实战解析

前阵子帮一位做跨境电商的朋友处理订单流程,他同时在四个平台开了店,每天要反复登录后台、导出订单、手工合并地址、再回填到 ERP 里。搞了一个月,他最大的体会不是单量有多大,而是重复操作能把人磨疯。后来我用 Agent Skills 配合…

📅 2026/10/17 12:39:40
MORE NEWS

更多资讯

📰

TypeScript+Canvas实战:构建完整贪吃蛇游戏及核心逻辑剖析

1. 项目概述与整体设计思路1.1 这个系列做到哪一步了先交代一下系列进度。上一篇文章里,我们已经用 TypeScript 搭好了项目的整体骨架,包括配置了 tsconfig、初始化了开发环境、创建了画布和基本的棋盘渲染逻辑。如果你一路跟着做过来,现在应…

📰

Unity生命周期消息机制:Start方法自动执行的底层原理

很多Unity开发者在入门阶段都会有一个挥之不去的疑问:我在脚本里写了个Start(),既没见谁调用它,也没有实现什么接口,凭什么它就被自动执行了?更神奇的是,你要是把Start改成MyStart,哪怕里面的业…

📰

Vue项目接入LDP本地打印服务:门店小票打印完整实践

做vue项目时接到一个很小的需求:门店小票打印。后台管理系统、收银机、订单列表都在Vue里跑得好好的,唯一的硬骨头就是点一下“打印小票”,要让顾客拿到一张排版像样、能扫出支付记录的热敏小票。一开始我以为浏览器里window.print()就能糊弄…

📰

CSS三角形原理与响应式实现全解析

1. 为什么一个三角形值得写满五千字?——从CSS边框机制说起你可能在某个深夜调试页面时,突然发现按钮右下角那个不起眼的小箭头,居然是纯CSS画出来的;也可能在重构一个老项目时,看到一段用border属性堆叠出的“小三角”…

📰

Vue3弹窗中Lottie动画被裁剪?根因排查与解决方案

最近做一个活动运营后台,需求方要求在弹窗里放一个 lottie 入场动画,本来以为是无脑接组件的活,结果一放进 el-dialog,动画要么只显示左上角一小块,要么右边被切掉一大截,极端情况下直接白屏。这个组合坑了…

📰

网易互娱游戏研发笔试全解析:题型、策略与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬