news 2026/10/2 1:34:48

359张街景车辆图数据集:YOLO训练实战与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
359张街景车辆图数据集:YOLO训练实战与避坑指南

简介:面向YOLO系列目标检测实战的车辆检测数据集,聚焦城市街道场景中的公交车、轿车、卡车、面包车四类目标,适用于目标检测算法训练与效果验证,适合入门至进阶的计算机视觉开发者直接使用。压缩包共1088个文件,包含367张原图、359个可扩展标记语言标注文件与359个文本标注文件,另有数据配置文件、说明文档和参考手册;图片与标注一一对应,内置数据配置文件已划分好训练集与验证集,可无缝对接多种主流目标检测算法版本。资源包约114兆字节,页面底部提供数据集可视化效果,便于训练前核查标注质量。目前已有8人学习下载。借助该数据集,读者无需从零整理数据即可开展模型训练、精度对比与调参实验,同时配套说明文档辅助上手,适合作为毕业设计、算法练习或项目落地的起步数据集。

1. 359张街景车辆图,够训练YOLO吗:这个数据集到底能拿来做什么

做目标检测的人第一次看到「359张」这个数量级,脑子里蹦出来的第一个问题通常是:这够训吗?答案是,直接从头训练一个YOLO模型,远不够;但用它来做微调、做验证集、做算法选型的基准测试、做标注格式的学习样本,非常合适。城市街道车辆检测这个任务本身就很有意思——公交车、轿车、卡车、面包车四类目标,街景背景复杂,遮挡、光照变化、远近尺度差异都齐了,是一个能真实暴露模型问题的场景,而不是那种干净到不真实的实验室数据集。

这个数据集的实际价值,不在于它的规模,而在于它是「城市街道 + 多类别车辆 + YOLO格式标注」这样一个组合。对刚入门目标检测的开发者来说,它是第一个能让你完整跑通「数据准备 → 格式转换 → 模型训练 → 指标评估 → 部署验证」全流程的小数据集,训练速度快,迭代成本低。对老手来说,这个小数据集适合做数据增强策略的对比实验,或者快速验证某个改进点是否有效。359张,四类目标,标注格式现成——这是一个能让你把精力放在调参和排错上,而不是浪费在等训练和改bug上的数据集。

2. 数据集里有什么:从目录结构到标注文件

2.1 拿到zip之后第一件事:解压和目录盘点

下载下来的是一个zip压缩包,解压之后,我建议你做的第一件事不是急着看图片,而是先把目录结构列清楚。常见的数据集打包方式有两种:一种是图片和标注文件混在一起,一种是按 train/val 分好目录。无论哪种,先跑一遍 tree 命令就能对全局有个判断。

# 进入数据集解压后的根目录 cd yolov8_vehicle_dataset # 看整体目录结构,注意深度控制,避免刷屏 tree -L 2 -d # 只看目录不看文件 find . -type f | wc -l # 统计文件总数,核对是否与描述一致

如果输出显示图片和标注混在同一个目录下,说明数据集没有做训练集和验证集的切分,后续需要自己手动划分。如果已经分好 train/val,那数据集发布者大概率已经考虑过验证的问题,可以省一步。但我建议你无论如何都自己重新划分一次,因为很多数据集的划分方式是随机的,没有考虑同一辆车在连续帧中出现的情况,这种泄露会影响评估结果的可信度。这个细节我们放到第4章展开。

检查完文件数量和目录结构后,下一个动作是抽样看几张图片和对应的标签,确认图片确实存在、标注文件确实和图片一一对应。这一步看似多余,但对于从网上下载的数据集来说,文件缺失和标注错位是家常便饭。

# 统计图片和标注文件各自的数目 ls images/ | wc -l ls labels/ | wc -l # 抽查一对图片和标注文件是否可以对应 head -n 5 labels/street_view_001.txt

这个命令的预期结果应该是:图片数和标签数完全一致。如果数量对不上,说明数据有问题,后面训练时会出现莫名其妙的报错。标注文件的内容格式,也能从这个命令里看出来——是归一化坐标的YOLO格式,还是VOC的xml格式,还是COCO的json格式,一眼便知。

2.2 四类目标在YOLO格式里怎么编码

YOLO格式的标注文件是txt,每行一个目标,五个数字依次是:类别序号、中心点x坐标(归一化)、中心点y坐标(归一化)、目标宽度(归一化)、目标高度(归一化)。拿这个数据集来说,四类目标的类别映射通常是:0-公交车、1-轿车、2-卡车、3-面包车,也可能是别的顺序,打开类别配置文件就能确认。

# 一个典型的YOLO标注文件,每行代表一个目标 0 0.482031 0.515313 0.105469 0.116875 1 0.296094 0.521458 0.061719 0.075625 2 0.726562 0.528438 0.068750 0.075000

上面第一行的含义是:类别0(公交车),中心点在图片的横向48.2%位置、纵向51.5%位置,目标宽度占图片总宽的10.5%,高度占图片总高的11.7%。坐标系是以图片左上角为原点,x轴向右、y轴向下,所有数值都在0到1之间。如果你打开标注文件发现坐标有大于1的数,或者有负数,这个数据集的标注格式就有问题,需要清洗后才能用。

归一化坐标的好处是,不管输入图片的尺寸是640还是1280,标注文件都不需要改变,这也是YOLO系列数据集的标准做法。不过需要注意的是,不同的YOLO版本对标注文件的期望略有差异,比如YOLOv5之前是每个txt独立存放,YOLOv8也延续了这个方式,但某些第三方实现要求标注文件与图片同名同路径,所以在把数据集导入框架之前,先确认一下目录结构是否匹配。

2.3 标注质量控制:小数据集最怕的是标注错

359张的规模,人工全部检查一遍标注是现实可行的,这也是小数据集在数据质量上的天然优势。一张一张过图,确认每个目标的框是不是贴合,类别有没有标错,是不是有漏标。这工作很枯燥,但小数据集的容错率低,一张图片的标注错误就能让训练结果产生明显偏差。

检查标注时一般用LabelImg、labelme或AnyLabeling这类工具直接可视化。如果原数据集已经带YOLO格式的标签,可以写一个Python脚本批量生成可视化验证图,把每个目标的框和类别名画到对应图片上,再快速翻看这批验证图,效率比一张张打开工具要高。

import cv2 import os # 读取YOLO格式标注并绘制到原图上,生成可视化检查图 def visualize_yolo_annotation(image_path, label_path, output_dir): img = cv2.imread(image_path) height, width = img.shape[:2] class_names = {0: 'bus', 1: 'car', 2: 'truck', 3: 'van'} with open(label_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() cls_id, x_center, y_center, w, h = int(parts[0]), *map(float, parts[1:]) # 归一化坐标转像素坐标 x1 = int((x_center - w / 2) * width) y1 = int((y_center - h / 2) * height) x2 = int((x_center + w / 2) * width) y2 = int((y_center + h / 2) * height) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.imwrite(os.path.join(output_dir, os.path.basename(image_path)), img) # 批量处理images目录下所有图片 for img_file in os.listdir('images'): if img_file.endswith('.jpg'): label_file = img_file.replace('.jpg', '.txt') visualize_yolo_annotation( os.path.join('images', img_file), os.path.join('labels', label_file), 'visual_check' )

这段脚本的逻辑是:遍历图片目录,对每张图找到对应的txt标注文件,把YOLO的归一化坐标转换成像素坐标,然后画框和类别名。运行之后,打开visual_check目录翻看图片,重点观察:框是否贴着目标边缘、有没有明显漏标的车辆、类别是否张冠李戴。这个检查过程花不了多少时间,但能避免你在后续训练中反复怀疑自己的模型不行,结果最后发现是标注错了。

我在实际项目中碰到过最典型的情况是:数据集中轿车和面包车在某个角度下非常像,标注员把面包车标成了轿车,导致模型在验证集上这一类的AP值特别低。只有可视化检查才能发现这类问题。

3. 把数据集喂给YOLOv8:完整训练流程与关键参数

3.1 从原始标注到可训练的YOLOv8数据集结构

YOLOv8的数据集目录结构固定,images和labels必须分开放,而且train和val要分开。如果你的zip包内的结构不是这样,就需要写转换脚本把它整理成YOLOv8期望的结构。这是整个流程里最机械但最不能出错的一步。

# 在ultralytics工程下创建数据集标准目录结构 data/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 训练标注 │ └── val/ # 验证标注 └── data.yaml # 数据集配置文件

整理思路:先把数据集按80/20的比例随机划分为训练集和验证集,划分的维度是「图片名」,做到图片和同名标注文件一起移动。这里有一个关键细节——划分之前先按某种方式对图片排序并保证随机性,不要直接取前80%当训练集,因为下载的数据集图片可能是按时间顺序排列的,直接切分会造成训练集和验证集分布不一致。

import os import random import shutil def split_dataset(root_dir, train_ratio=0.8, seed=42): # 固定随机种子,保证实验可复现 random.seed(seed) image_dir = os.path.join(root_dir, 'images') label_dir = os.path.join(root_dir, 'labels') train_img_dir = os.path.join(root_dir, 'split', 'images/train') val_img_dir = os.path.join(root_dir, 'split', 'images/val') train_lbl_dir = os.path.join(root_dir, 'split', 'labels/train') val_lbl_dir = os.path.join(root_dir, 'split', 'labels/val') # 创建目标目录 for d in [train_img_dir, val_img_dir, train_lbl_dir, val_lbl_dir]: os.makedirs(d, exist_ok=True) all_files = [f for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png'))] random.shuffle(all_files) split_idx = int(len(all_files) * train_ratio) train_files = all_files[:split_idx] val_files = all_files[split_idx:] # 移动图片和对应标注文件 for f in train_files: shutil.copy(os.path.join(image_dir, f), os.path.join(train_img_dir, f)) label = f.rsplit('.', 1)[0] + '.txt' if os.path.exists(os.path.join(label_dir, label)): shutil.copy(os.path.join(label_dir, label), os.path.join(train_lbl_dir, label)) else: print(f'警告:训练集缺少标注 {label}') for f in val_files: shutil.copy(os.path.join(image_dir, f), os.path.join(val_img_dir, f)) label = f.rsplit('.', 1)[0] + '.txt' if os.path.exists(os.path.join(label_dir, label)): shutil.copy(os.path.join(label_dir, label), os.path.join(val_lbl_dir, label)) else: print(f'警告:验证集缺少标注 {label}') print(f'训练集:{len(train_files)}张,验证集:{len(val_files)}张') if __name__ == '__main__': split_dataset('/path/to/dataset')

这段脚本的意图是把原始数据切成YOLOv8的标准结构。首先用固定随机种子打乱文件顺序,再按比例切分,最后把图片和标注文件对应拷贝到各自的train/val目录下。运行后的输出会告诉你切分结果,同时打印警告——有图无标注的文件名。警告不是致命问题,但你要去判断:如果缺失标注的图片恰好是验证集里的某些难例,会对结果评估产生多大影响。

接下来要写data.yaml文件,这是YOLOv8读取数据集信息的入口:

# data.yaml,注意路径使用绝对路径或相对于ultralytics运行目录的相对路径 path: /absolute/path/to/data # 数据集根目录 train: images/train # 训练图片路径相对于根目录 val: images/val # 验证图片路径相对于根目录 # 类别数和类别名,顺序必须与标注文件中的数字一一对应 nc: 4 names: 0: bus 1: car 2: truck 3: van

这个yaml文件是整个流程里最容易写错的地方。最常见的错误是path路径写错、train和val拼写错误、names顺序和标注文件里不一致。路径写错的话,YOLOv8会在训练开始时报FileNotFoundError;类别名顺序错了,训练不报错但最后所有指标都是错的。建议把yaml文件里的path写成绝对路径,省得在相对路径上花时间排错。验证yaml文件是否正确的办法是跑一次模型快速验证一下,看能否成功加载图片和标注。

3.2 训练启动:YOLOv8命令行参数与首次运行

YOLOv8的API有两种用法:命令行和Python脚本。入门阶段用命令行最直观,改动参数只需要编辑一行命令。训练脚本如下:

# 在ultralytics环境内启动训练 yolo train \ data=/path/to/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20

选择yolov8n.pt作为预训练权重,而不是从零开始训练,原因很实际:这个数据集只有359张图片,从零训练不仅慢而且容易过拟合。yolov8n是YOLOv8系列里最小的模型,参数量约3.2M,在CPU上也有训练的可能性,GPU上几十秒就能跑完一个epoch。epochs设100看起来大,但配合patience=20的早停策略,实际训练到40到50轮就会因为验证集mAP不再提升而自动停止,不会浪费时间。

关键参数说明:

  • imgsz=640是YOLOv8默认输入尺寸,这个数据集的图片如果分辨率超出640,模型会自动做letterbox缩放,不会破坏长宽比
  • batch=16需要根据显存调整。6GB显存用16没压力,2GB显存建议减到4
  • lr0=0.01是初始学习率,配合YOLOv8自带的warmup策略,对预训练模型微调来说是一个稳妥的起点
  • patience=20的意思是连续20个epoch验证集指标没有提升就停止,这是防止过拟合的保险丝

训练过程会实时打印每个epoch的训练损失、验证损失、mAP50、mAP50-95、精确率、召回率等指标。看这些数字时要有预期:由于训练集只有287张左右(80%切分),模型很快就能把训练损失压到很低,验证损失会有波动。重点关注验证集mAP50是否持续上升,如果上升但速度很慢,说明学习率偏低;如果验证损失开始回升而mAP还在涨,说明开始过拟合,可以考虑提前终止。

第一次训练结束后,最快的验证方式是直接跑验证集:

yolo val \ model=runs/detect/train/weights/best.pt \ data=/path/to/data.yaml

作用是加载训练过程中验证集损失最低的那个检查点文件best.pt,在验证集上重新计算所有指标。输出会打印每个类别的AP50、AP50-95等。对4类车辆来说,如果轿车AP50超过0.8,卡车相对低一些,是一个合理的预期——轿车外观稳定样本多,卡车外观变化大且容易被公交车遮挡,指标偏低是正常现象。

3.3 识别效果评估:光看mAP不够,要逐类看

mAP是宏观指标,逐类AP才暴露出模型真正的问题。用上面val命令的输出或者从results.csv里读取数值,每类的AP差异会告诉你模型对哪类目标学得不好。如果面包车AP明显低于其他三类,大概率是标注数据里面包车样本偏少,或者标注时存在类别混淆。

import pandas as pd # 读取训练过程生成的指标文件 df = pd.read_csv('runs/detect/train/results.csv') # 打印最后10轮的mAP50和mAP50-95走势 print(df[['epoch', 'metrics/precision(B)', 'metrics/recall(B)', 'metrics/mAP50(B)', 'metrics/mAP50-95(B)']].tail(10))

这段代码的功能是从results.csv文件中提取训练最后10轮的指标,帮助你判断训练是否收敛——mAP50不再明显上升说明模型已经学到能学的了。如果最后几轮的mAP仍在持续上升,说明epoch数不够,可以把epochs调得更大;如果最后几轮mAP下降了,说明过拟合已经发生,需要对数据增强做限制或增加验证集数据。

单个模型的mAP可以说明问题,但更彻底的做法是拿几张没参与训练的真实街景图片做实测推理对比:模型预测出的框和类别,和你自己眼见为实的情况是不是一致。这一步是验证模型真实泛化能力的最佳手段,比任何指标都有说服力。

4. 四类车辆数据集的典型坑与排查方法

4.1 标注泄露:连续帧图片被同时分到训练集和验证集

现象:训练时loss一路下降很顺利,验证集mAP也很高,但把模型拿到真实的街道视频里做测试,效果明显比验证集指标差,误检和漏检都变多。

原因:下载的数据集往往是从视频抽帧得到的,同一辆车在相邻帧里反复出现。如果切分时直接随机划分,同一辆车在不同帧上的标注会同时出现在训练集和验证集里,模型相当于提前见到了测试时的答案,这就是标注泄露。验证集指标虚高,但实际泛化能力并没有那么好。

解决:拿到数据集后先按图片的文件名判断来源,如果发现连续编号的图片可能来自同一个视频流,按文件名的前缀或时间戳做分组,把同组图片整体划分到训练集或验证集,避免让同组帧被拆散。这个检查看起来不起眼,但对评估结果的真实性质影响非常大。

4.2 训练时loss显示nan,概率是学习率过大

现象:训练刚开始几轮,loss就变成nan,之后一直nan,mAP始终是0。

原因:这个数据集只有359张,样本少,梯度本身波动就大。如果初始学习率设得过高(比如lrf=0.1配合lr0=0.01在某些情况下会导致梯度爆炸),模型权重直接发散,loss变成nan。

解决:把lr0降到0.001,或者开启YOLOv8的余弦退火调度器来平滑学习率变化。另一个更稳的办法是换用yolov8n.pt的预训练权重来训练,预训练模型的参数空间已经被约束在合理范围内,微调时的鲁棒性远高于从零开始。调低学习率后如果loss仍然出现nan,通过torch.autograd.set_detect_anomaly(True)定位到具体是哪一行网络层产生的问题,这是最笨但最有效的排查手段,总比你反复拍脑袋调参数强。

4.3 类别不均衡:卡车样本少导致AP上不去

现象:逐类AP统计里,公车的mAP50在0.9左右,卡车的mAP50只有0.5出头。训练过程里看到backbone已经在努力,但卡车的检测框就是不稳定,晃动明显。

原因:四类车辆在街道场景中的出现频率天然不同。轿车最多,出租车、网约车、私家车遍地都是;卡车出现的频率相对低很多,标注框数量可能只有轿车的十分之一,模型学不够。

解决:优先做法是检查标注里各类目标的框数量分布,确认卡车的样本量到底是多少。然后做数据增强补样本,对已有的卡车样本做复制粘贴增强——把卡车的目标块随机粘贴到其他图片中,或者做水平翻转、尺度变化来扩充。更简单的做法是使用YOLOv8的class_weight参数给少数类更高的损失权重,让模型在训练时对卡车更敏感。这一步做完,卡车的AP通常会有明显改善。

4.4 验证集指标虚高但实际预测很拉胯:别忘了看误检

现象:验证集mAP50到了0.85,但用训练好的模型对真实街景视频逐帧检测,发现模型把路边的公交站牌误检成公交车,把大型SUV误检成面包车,甚至还有把一堵墙上的广告车图误检成真车。

原因:数据集在采集时存在bias,街景图里的车辆大多在近处和中间位置,远景车辆很少,导致模型对尺度变化不敏感。加上标注时可能把卡车和面包车混淆过,模型学到的类别边界就不清晰。

解决:用训练好的模型跑验证集之外的真实图片,把误检和漏检的cases收集起来,分析是画框位置不对还是类别判错。如果误检集中在类别判断上,回查对应的训练标注是否有错标;如果误检集中在远处小目标上,把imgsz提高到960再重新训练,同时确认训练图片里是否大量包含小目标——如果数据集本身几乎全是近景大目标,光调imgsz也没用,需要补充不同距离、不同尺度的车辆样本。

5. 用这359张图榨出更好模型的具体技巧

5.1 用预训练权重和冻结训练

把YOLOv8在COCO上的预训练权重当成一个已经在海量数据上学过通用视觉特征的模型。冻结backbone的前几层不让它们更新,只训练网络后部的检测头来适配四类车辆的检测,这种做法在数据量不足时特别有效,训练速度也更快。

# 冻结前10层backbone,只训练检测头 yolo train \ data=/path/to/data.yaml \ model=yolov8s.pt \ freeze=10 \ epochs=50 \ imgsz=640 \ batch=16

freeze=10的含义是冻结模型前10层,这些层通常包含低级别的通用特征提取器——颜色、边缘、纹理等。对微调来说,保留这些通用特征可以避免灾难性遗忘,让模型专注学习车辆的类别判别。等第一批训练结束后,如果验证集指标还一直在涨,再解冻全部层做5到10轮的微调,学习率调低到0.0001,这样收敛得更稳。这就是我自己常用的两阶段训练法,对小数据集训练的效果比直接训练到底好得多。

5.2 半自动标注:把模型预测结果反哺成新训练数据

模型在验证集上达到可用水平后,拿它去跑一批没有标注的街景图片,把置信度较高的预测结果直接当成伪标注,人工修正后加入训练集。这实际上是模型辅助人工标注的流程,能极大扩展数据集规模。

# 用当前模型对未标注图片做批量推理,输出YOLO格式的预测结果 yolo predict \ model=runs/detect/train/weights/best.pt \ source=/path/to/unlabeled/images \ save_txt=true \ conf=0.6 \ max_det=20

conf=0.6是关键参数——置信度阈值。设高一点,预测结果中误检的比例会低很多,人工修正成本低;设太低,伪标注里混入太多错的框,会污染模型训练。保守起见,我通常从0.7开始,宁可漏检也不要误检。预测生成的txt文件放在predicted_labels目录下,导入标注工具里逐张检查修正,重点看那些0.6到0.8置信度区间内的框,这是最容易出错的地方。

这种做法迭代两到三轮,数据集规模可以翻倍。对359张的数据集来说,三轮半自动标注扩到800到1000张是可行的工程目标。每次扩充后重新训练,mAP通常能看到稳定回升,模型的泛化能力比单靠359张硬撑出来的效果好很多。

5.3 各类目标踩坑的经验来源:任何小数据集都要做的多组重复实验

训练实验一定要做至少三组,每次换随机种子,对比三组实验的平均值和方差,才能判断一个参数改动到底是真有效还是碰运气。小数据集本身样本少,随机性对训练结果的影响被放大,某一次训练的mAP突然涨了0.05,不代表你的改动有效,很可能只是随机切分恰好把简单样本分到了验证集。

我之前在一个类似的小数据集上踩过这个坑。当时为了提升卡车的AP,加了一组针对卡车的弱标注数据,训练后mAP涨了4个点,我一度以为自己的方案成功了。后来换种子重跑,发现涨幅缩水到不显著。差点就把一个无效的改动写进方案里。从那以后,凡是低于1000张的数据集训练,我起步就是三组并行实验,这是对数据和结果的基本尊重。修改任何参数前,先看清它到底改的是什么——学习率影响梯度更新步长,freeze影响特征提取层是否更新,conf只影响预测时的置信度阈值,不改变模型本身的能力。搞清楚这些边界,能替你挡掉大量重复实验的时间成本。希望这些从实践中磨出来的经验能帮到你,让你在这条路上少绕几个弯。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 1:33:12

Multisim仿真24秒倒计时电路:555秒源与74LS192预置设计全解析

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

作者头像 李华
网站建设 2026/10/2 1:33:09

真值表到SOP/POS:数字逻辑设计核心化简与HDLBits实战指南

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

作者头像 李华
网站建设 2026/10/2 1:33:07

工程师之路:编程技能、项目实战与面试求职的成长指南

说句实在话,刚工作那几年我特别不想谈“工程师之路”这种题目。总觉得它太宏大,自己那点经历根本不配写。后来陆陆续续带过十几个新人,也回答了不少想转行朋友的私信,才慢慢发现:太多人缺的并不是聪明和努力&#xff0…

作者头像 李华
网站建设 2026/10/2 1:32:51

柔软线防断传感器选型指南:光纤对射与电容式方案实战

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

作者头像 李华
网站建设 2026/10/2 1:32:17

SpringBoot体质测试系统:从Excel沉睡数据到运动处方

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

作者头像 李华
网站建设 2026/10/2 1:32:11

梯形图:工业控制中人机协同效率最高的编程语言

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

作者头像 李华