做自动驾驶感知的第一天,带队的老工程师就跟我说过一句话:项目上没有花活,车辆、行人、红绿灯,这三类目标能不能稳定检出来,基本决定了一套系统能不能过路测。后来我在某量产L2辅助驾驶的感知模块里,真的把YOLOv8搬上了车,跑在常见的车载计算平台上,从一开始的纸上谈兵到最终稳定输出检测框,中间踩过的坑多得可以写满一页纸。这篇文章我按项目推进顺序来写:先想清楚为什么选YOLOv8,再说数据、训练、部署这些环节怎么逐层做扎实,最后把实车路测时遇到的问题和排查方法原原本本列出来。如果你正在做车载感知落地,或者准备做一个自动驾驶目标检测的项目复现,这套思路可以直接拿来参考。
我当时定下两条底线:一是检测延迟必须稳定,中位数好看没用,尾延迟抖动会直接影响规控;二是小目标召回不能只靠降阈值,要把红绿灯和远处的行人真正捡回来。围绕这两个目标,YOLOv8的n和s版本成了最顺手的工具。但模型选型只是第一步,后面数据标注、量化部署、线程调度,任何一个环节没做好,模型精度再高也白搭。
1. 为什么自动驾驶实时检测场景偏偏选中YOLOv8
1.1 车辆、行人、红绿灯这三类目标的检测难点
刚开始做这个项目的时候,我以为检测任务无非就是框出目标,真正把三类目标的数据分布拉出来看才发现差别非常大。车辆是三类里最容易检测的,目标体积大、边缘清晰、外观纹理丰富,但问题在于形态跨度极大——轿车、SUV、卡车、公交车、拖挂车,还有各种改装车型,同一个类别内部的特征差异比很多跨类差异还大。更麻烦的是遮挡和截断,路口场景里车辆经常互相遮挡,只露出半个车头或一条货箱边缘。
行人属于中等难度,特征是尺度变化非常剧烈。同一个行人,在1080p画面里可能占据200像素高度,也可能只有40像素,远处行人在640分辨率的输入图上可能就剩十几个像素。另外行人姿态复杂,站立、骑行、弯腰、携带物品,模型很容易在密集人群场景里漏检或重复框。
红绿灯是最特殊的一类,它不只是小目标,还带着颜色语义。现实中的红绿灯像素占比极小,在1080p画面里一个灯体通常只有20到40像素,缩到640分辨率后基本就是一团颜色点。而且红绿灯有状态变化,同一个灯在不同帧里分别是红、绿、黄、灭,检测器如果直接对颜色分类,类别不平衡和状态切换带来的噪声会非常致命。加上夜间发光、白天反光、逆光过曝,红绿灯的误检率在整个项目里一直是重点盯防对象。
目标特性决定了模型选型的关注点:模型不能太大,要留出算力给预处理、后处理和规控通信;但小目标召回能力又不能弱,否则红绿灯直接崩塌。这正好落在YOLOv8n和v8s的射程范围内。
1.2 YOLOv8与同类型模型的差异在哪里
选型阶段我其实是把几个主流方向都过了一遍,不是一上来就锁死v8。对比下来,YOLOv5的生态非常成熟,部署资料多,但静态anchor机制需要针对数据集做聚类,在交通灯这类小目标上anchor参数本身就会引入额外调参成本。YOLOv9和后续版本在精度上有提升,可导出、量化、部署的工具链成熟度在当时还差一口气,车载项目要的是确定性和可复现性,模型不是越新越好。RT-DETR这类端到端方案精度很棒,也省了NMS,但transformer结构在嵌入式平台上的显存占用和延迟还是偏高,做L2辅助驾驶的盒子吃不消。
表格化对比更直观:
| 方案 | 部署生态成熟度 | 小目标表现 | 后处理复杂度 | 量化友好度 | 嵌入式平台适配 |
|---|---|---|---|---|---|
| YOLOv5 | 很成熟 | 一般,依赖anchor配置 | 有NMS,anchor解码 | 较好 | 优秀 |
| YOLOv8n/s | 成熟且持续更新 | 较好,anchor-free天然适配 | 有NMS,无anchor解码 | 较好 | 优秀 |
| YOLOv9/10/11 | 逐步完善 | 有提升 | 有NMS | 中等 | 中等 |
| RT-DETR | 一般 | 好 | 无需NMS | 一般 | 偏紧 |
最终选择YOLOv8n作为主力模型、v8s作为备选,原因有三个:anchor-free设计让检测头不再依赖预设框,长期维护成本低;模型结构对TensorRT的算子融合友好,FP16和INT8都能稳定跑;官方开源库的训练、导出链路完整,从PyTorch到部署引擎的转换基本没有断点。
1.3 具体架构细节:C2f、解耦头、DFL到底在做什么
选YOLOv8不能只看名气,得清楚它内部每个模块在车载场景里到底帮了什么忙。C2f模块是YOLOv8的核心特征提取结构,可以理解成把不同深度的梯度流拆成多条支路再合并,让信息在深层和浅层之间传得更顺畅。在车辆遮挡场景下,这种多级特征融合能让模型同时利用车身的整体轮廓和局部细节,不至于被遮挡区域打乱判断。
解耦头是另一个关键设计——分类和回归分支分开。传统耦合头把“这是什么”和“框在哪”放在同一个输出层,训练时两个任务会互相干扰。解耦之后,分类分支可以专注于区分车辆和行人,回归分支专注于框的边界精度,对红绿灯这种既要分清颜色位置、又要稳定锁住边界的场景提升很明显。
DFL损失函数把边界框回归做成了离散分布预测,通俗说就是让模型对“边界落在哪个像素区间”有一种带不确定性的判断。对行人这种边缘模糊的目标,它能给出更稳健的框。TaskAlignedAssigner负责分配正负样本,同时考虑分类置信度和IOU质量,比单纯看IOU更合理。还有一个容易被忽略的点:YOLOv8的检测头在训练时做了多次上采样融合,天然对小目标更友好,这对红绿灯检测极其重要。理解了这些模块,后面做剪枝、量化、换激活函数时才不会两眼一抹黑。
2. 数据准备与标注策略:先解决小目标问题
2.1 训练数据的构成与自采补充
模型选型做完,我本想直接用公开的自动驾驶数据集训练,后来发现公开数据集虽然量大,但和实际路况存在不小的分布差。典型的例子是:公开数据里白天晴天的样本占比很高,而实际路测最怕的恰恰是夜间、隧道、雨天、逆光,这些场景下红绿灯和行人都会呈现完全不同的视觉特征。
所以我的做法是“公开数据打底、自采数据补短板”。公开数据用来让模型学到通用的车辆轮廓和行人姿态,自采数据则针对目标场景做定向补充——同一路段往返采集不同时段的数据,专门覆盖清晨逆光、傍晚黄昏、夜间路灯和雨天反光这些边界条件。自采数据不需要贪多,但要保证每一段都覆盖到检测任务真正容易出错的角落。
数据量方面,我建议车辆类样本最少在5万张以上,行人不少于3万张,红绿灯尽可能做到1万张以上。如果红绿灯样本不够,直接决定后续训练的上限,因为这类目标又小又带状态变化,模型没有充分见过就很难在实车场景里无中生有。
2.2 类别定义和标注规范的工程约定
很多项目在类别定义上不够较真,直接把“车”“人”“灯”三个大类扔给模型,结果训练出来的模型在卡车和公交车之间反复横跳。车载感知要给规控提供可用的目标类型,类别应该拆分清楚。
我当时的类别集合大致如下:
| 类别 | 说明 | 备注 |
|---|---|---|
| car | 轿车、SUV、MPV等小型车 | 主流目标 |
| bus | 公交车、中巴 | 和卡车分离 |
| truck | 卡车、拖挂车 | 形态差异大 |
| motorcycle / bicycle | 两轮车 | 骑行人和车一起框 |
| pedestrian / rider | 行人与骑行者 | 分开建模 |
| traffic_light_red / green / yellow / off | 红绿灯状态 | 检测网络只出框,颜色状态由后端逻辑处理 |
这里有个经验:红绿灯不建议直接在检测网络里做细分分类。检测网络只需要输出灯体位置和置信度,颜色状态交给专门的分类网络或时序状态机来做。原因很简单,一个灯体的颜色是随时间变化的,同一个位置今天亮红、明天亮绿,检测网络里的类别语义会漂移,而后端状态机可以通过帧间关联确定一个灯当前的颜色和跳变逻辑,误报率低得多。工程量看着大了一点,但实车跑起来稳定性完全不一样。
标注规范也要提前定死。红绿灯的框不要只框发光点,建议把整个灯体外壳框住,避免夜间过曝或白天反光导致框的大小波动。行人被遮挡时要按可见部分标注,完全隐藏在车后的不标。车辆类别里,拖挂车要把拖斗算进整体框中,不能只框车头。
2.3 哪些数据增强要保留、哪些必须关掉
数据增强不是一律拉满就好,尤其是带颜色语义和几何完整性的目标。Mosaic增强把四张图拼在一起,对车辆和行人能增加背景多样性,但对红绿灯并不完全友好——合成图中灯体边缘会被撕裂,颜色也会被拼接边界干扰。更麻烦的是,Mosaic在训练后期会让模型反复看到不自然的图像结构,导致收敛不稳定。我最后的策略是前80轮epoch开Mosaic,最后10轮彻底关掉,让模型回归到真实图像分布上微调。
HSV颜色增强也要做特殊处理。YOLOv8默认开启的HSV增强会随机改变色相和饱和度,对车辆行人没大问题,但红绿灯的核心特征就是颜色,色相一抖动,红灯变绿、绿灯变黄,模型直接学歪。我的做法是单独关掉红绿灯相关样本的色相增强,只保留轻微亮度扰动。
对车载场景来说,还有一个非常管用的增强叫随机仿射变换,通过小幅旋转和缩放模拟车辆行驶过程中目标的视角变化。配合Copy-Paste增强,把真实车辆目标粘贴到空旷路面背景上,能有效提升卡车、拖挂车这些长尾类别的样本量。需要注意的是,增强参数不能太激进,旋转角度超过15度后目标外观失真严重,反而降低实车性能。
2.4 长尾分布与难样本挖掘
训练完第一版模型之后,最该做的不是继续调参,而是把模型的错误拉出来做难样本分析。我用现场采集的原始视频对模型做批量推理,把漏检和误检的片段都截出来,按场景归类。结果发现夜间行人的漏检绝大多数集中在逆光过曝路段,红绿灯误检集中在雨后路面反光区域,车辆漏检集中在卡车斜侧视角。这些错误信息比任何指标都真实,也是数据迭代的切入点。
难样本挖掘的闭环是:先训练初始模型,对自采长尾场景做预标注,找到漏检目标后人工补充标注并加入训练集。这个循环跑两到三轮,夜间行人和红绿灯的召回会有肉眼可见的提升。我见过很多团队花大力气调网络结构,却忽略难样本挖掘能带来的收益,实际上在自动驾驶感知这类数据驱动型任务里,高质量难样本对模型的提升经常比换一个更强的骨干网络还大。
3. 训练与评估:怎么从高mAP变成能上车的模型
3.1 训练超参数和两阶段策略
训练细节对车载场景的影响很大,但很多教程只是甩一句“用默认参数训练”。我实际跑下来的经验是,在YOLOv8默认超参数基础上,针对自动驾驶数据集要做几处明确调整。输入分辨率固定为640x640,这是效率和精度的平衡点,后面我会单独讲分辨率选择。批次大小根据显存来,单卡能塞32就先用32,AdamW优化器配合权重衰减0.0005,初始学习率0.01加前3轮warmup,总训练轮数设定在200到300之间。
训练节奏建议分成两个阶段。第一阶段冻结backbone,只训练检测头,用小学习率跑10轮左右。这样做的目的是让检测头先在真实图像特征上做适配,不至于一上来就把预训练权重里的通用特征打乱。第二阶段解冻全部层,恢复常规学习率完整训练。这个策略在自采数据量不足时特别管用,能显著减少过拟合。
另外,我在实验里验证过使用预训练权重的重要性。在不使用预训练权重的情况下,红绿灯的AP在同等数据量下低了接近一半。车载数据虽然丰富,但很多场景特征仍然需要借助大规模数据集的通用特征来迁移,所以只要条件允许,别省这一步。
3.2 评估口径与验收标准不能只看mAP
模型训练完之后,先别急着部署,要把评估口径定清楚。纯mAP数字只能说明整体水平,落地验收必须看分项指标。我的习惯是要求车辆类别的mAP50不低于85,行人mAP50不低于80,红绿灯AP50不低于75,同时把极端场景单独拉出来看——夜间行人AP、雨天车辆AP、逆光红绿灯AP,这些才是真实路测的生死线。
延迟指标也有讲究。车载项目要看的是p95甚至p99延迟,而不是平均值。平均值容易被偶发的空闲帧拉低,规控系统真正感知到的是尾部延迟,偶尔一次200毫秒的卡顿可能比稳定60毫秒更危险。我在整个项目里坚持用端到端延迟做验收:从摄像头图像时间戳开始,到检测框输出时间戳结束,中间包括采集、预处理、推理、后处理、通信,任何一个环节抖动都会体现在尾延迟上。
还有一个容易被忽略的验收指标是显存峰值。盒子上的显存是固定的,模型占多少、预处理缓冲占多少、NMS中间结果占多少,都要提前算清楚。我遇到过模型精度和延迟都达标、结果一上盒子就OOM的情况,就是因为只看网络推理显存,忽略了整条pipeline的缓冲叠加。
3.3 轻量化手段:剪枝、蒸馏、激活函数替换
车载平台算力有限,光靠模型本身的体量往往不够,需要再做轻量化。经验排序是:蒸馏和剪枝的收益最稳,激活函数替换次之。我用一个精度更高的变体模型做教师,把知识蒸馏给YOLOv8n,蒸馏后的学生模型在保持原来推理速度的前提下,红绿灯小目标AP能提升两到三个点。蒸馏的核心是让软标签告诉学生模型哪些特征值得关注,对小目标来说尤其有效。
结构化剪枝做的是把不重要的通道剪掉。YOLOv8的BN层缩放因子可以直接用来衡量通道重要性,保留大的、剪掉小的,然后微调恢复。这里要注意剪枝比例不能一下拉太高,我建议从15%到20%起步,剪完一个比例就做一次完整评估,不能盲目追求模型瘦身。
激活函数替换方面,SiLU在FP16推理下性能很好,但INT8量化时动态范围偏大,容易引起精度回退。如果目标平台必须跑INT8,可以考虑把部分SiLU换成ReLU并做少量微调,ReLU的动态范围更窄,量化误差明显更小。不过这个操作要谨慎,整个网络全换可能会掉精度,建议量化完再按层做对比测试决定保留哪些层。
3.4 输入分辨率与推理分辨率的选择
输入分辨率是个两难问题:640x640在效率上最均衡,但对红绿灯小目标不够友好;拉到1280x1280能让小目标AP涨不少,延迟也跟着涨回去。我在项目里的解法是训练用640,但保留额外的测试尺度在960或1024上做验证,让模型对分辨率变化有适应能力。
部署时如果想兼顾小目标和实时性,可以用“检测加追踪”的工程思路弥补:检测网络维持640,红绿灯和远距离小目标交给专门的ROI切图检测分支。也就是说,画面中放大远程区域,切出局部再送一次检测。这样整体延迟增加不多,小目标检出率却会有明显提升。另一个选择是使用P2小目标检测头,YOLOv8的P2变体会在更大特征图上做预测,红绿灯AP提升明显,代价是延迟和显存增加,具体要不要用取决于盒子的性能余量。
4. 车载部署:从PyTorch到TensorRT的全链路落地
4.1 模型导出与Engine构建
训练只是开始,真正上车前的第一道门槛是把PyTorch模型转成推理引擎。我走的路是PyTorch导出ONNX,再做图简化和算子融合,最后用TensorRT构建engine。这个流程每一步都有坑:导出时要把检测头拆开,别把整个后处理也导出进去,后处理更适合留在部署侧手写,灵活性更大。
ONNX导出之后建议跑一遍图简化工具,把多余的transpose和reshape清掉,然后用固定尺寸构建TensorRT engine。固定shape比动态shape更适合车载场景,动态shape会在运行时引入额外的显存碎片和调度开销,固定640x640能最大程度保证延迟稳定。构建engine的时候打开FP16精度,这是车载部署默认选择,能在精度损失极小的情况下带来接近翻倍的速度提升。
import tensorrt as trt logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser = trt.OnnxParser(network, logger) with open("yolov8n.onnx", "rb") as f: parser.parse(f.read()) config = builder.create_builder_config() config.set_memory_pool_size(trt.MemoryPoolType.WORKSPACE, 1 << 30) config.set_flag(trt.BuilderFlag.FP16) engine = builder.build_serialized_network(network, config) with open("yolov8n.engine", "wb") as f: f.write(engine)这段代码看起来简单,但build的环境要提前固定。同样的ONNX在不同版本的TensorRT上build出来的engine性能可能差很多,所以项目一开始就锁定版本,后面所有联调都在同一套环境里进行,避免版本漂移带来的排查困难。
4.2 INT8量化与精度回退
FP16跑顺之后,如果延迟还差一点,才考虑INT8量化。INT8能让推理速度再上一个台阶,但前提是校准和精度验证做扎实。校准集不是随便抽几百张图就完事,要严格覆盖白天、夜间、隧道、雨天、逆光、黄昏等工况,每类目标都要有足够样本,尤其是红绿灯的各个状态。
INT8的量化误差来源主要是激活分布的长尾。YOLOv8里DFL分支和部分SiLU激活的动态范围比较大,直接全部量化可能会让红绿灯小目标AP掉三到五个点。排查方法是对比每层FP16和INT8的输出差异,敏感层单独保留FP16,其余层继续INT8。这种混合精度策略能在速度和精度之间取得最好的平衡。
# 用校准集生成INT8 calibrator并构建引擎 trtexec --onnx=yolov8n.onnx \ --calib=calibration_images \ --int8 \ --saveEngine=yolov8n_int8.engine校准结束后必须重新跑一遍完整的评估集,而且要重点验证夜间样本。我遇到过校准集只抽了白天场景,int8量化后红灯AP看着没跌,一上实车夜间路段漏检暴增的情况,原因就是校准数据没有代表夜间的亮度分布,激活范围估算偏了。
4.3 预处理、后处理与线程化推理管线
部署后的优化重点不在网络本身,而在前后处理和调度。很多人在Jetson上发现推理只占10毫秒,但整条pipeline的帧率还是上不去,问题几乎都出在CPU端的预处理和后处理阻塞上。
预处理里的letterbox、BGR转RGB、归一化,如果全在CPU上做,每帧都要占用几毫秒,双摄像头时还会互相抢CPU资源。我的做法是全部改成CUDA算子,在GPU上完成,CPU只负责读帧和推流。后处理里的DFL解码和NMS也要优化,NMS用EfficientNMS插件合并进engine,这样解码和抑制都在GPU上一次性完成,避免把检测结果拷贝回CPU再做,既省时间又省带宽。
线程模型可以参考流水线架构:采集线程、预处理线程、推理线程、后处理线程、应用线程之间用有界队列解耦。不要一帧一帧阻塞等结果,而是让每一路摄像头独立进入流水线,推理线程通过流调度并发处理。实测下来双路1080p输入时,这种架构能把端到端延迟的抖动压下来,p95比单线程方案低一半左右。
4.4 端侧实测数据与功耗热管理
部署完成后一定要做整机实测,记录真实环境下的数据。我当时的测试环境是常见的嵌入式AI计算平台Orin NX档位,YOLOv8n FP16模型在640输入下纯推理延迟约7到9毫秒,INT8约5到6毫秒,端到端p95延迟控制在35毫秒以内,双摄像头同时工作时的显存峰值约2.3GB,功耗在25瓦到40瓦之间波动。
不同精度与分辨率的实测对比:
| 配置 | 纯推理延迟 | 端到端p95 | 显存峰值 | 小目标AP相对值 |
|---|---|---|---|---|
| v8n FP16 640 | 7-9ms | 30-35ms | 1.8GB | 基准 |
| v8n INT8 640 | 5-6ms | 25-28ms | 1.6GB | 略降 |
| v8s FP16 640 | 14-18ms | 40-45ms | 3.1GB | 提升5-8% |
| v8n FP16 960 | 12-15ms | 45-50ms | 2.6GB | 提升2-3% |
功耗和热降频是实车测试里最容易翻车的点。长时间满负载运行时,盒子的风扇策略如果不够激进,芯片温度会顶到降频阈值,推理延迟会突然从8毫秒跳到20毫秒,表现成周期性卡顿。解决办法是固定GPU时钟频率,或者把风扇策略改成更早介入的温控曲线。单纯锁频会牺牲峰值性能,但换来的延迟稳定性对自动驾驶来说往往更重要。我在整车测试时更偏爱“锁定MAXN频率+主动散热”的组合,让性能始终保持在一条水平线上。
5. 实车路测中最常踩的坑与排查实录
5.1 红绿灯误检漏检:状态分离才是治本
实车路测第一周,红绿灯就是最大的问题来源。常见误检发生在雨后黄昏,路面反光带被模型识别成红色灯体;夜间LED屏幕和刹车灯也会被误检成红灯。排查后发现,问题不在于模型能力,而在于我把颜色分类塞进了检测网络。把所有交通灯类别统一合并成一个“灯体目标”后,误检率立刻降了一大半。
漏检则多集中在远距离小目标。640输入分辨率下,80米外的红绿灯可能只有几个像素,模型根本感知不到。我的解法是后端对红绿灯用检测加追踪加状态机:检测网络负责在ROI区域捕捉潜在的灯体目标,追踪器负责跨帧关联,状态机根据连续帧颜色变化输出当前状态。这个组合下来,远距离小目标虽然偶尔漏检,但通过多帧时间滤波能稳定输出,实车表现比单帧检测提升显著。
5.2 夜间行人召回不够:曝光和数据集要双管齐下
夜间行人漏检的路测结果一直不理想,翻看数据才发现两个叠加因素:一是摄像头在夜间为了兼顾整体画面,经常把远处行人压到接近纯黑;二是训练数据里夜间行人占比本身就低。模型没见过足够多的“黑暗中的行人”,自然无法形成稳定的特征表达。
处理思路也分两条腿。数据层面,专门挑夜间无路灯或弱路灯路段补充采集,把远光、近光、行人打伞、骑行等场景都覆盖到,并进行曝光增强预处理。模型层面,在推理时对低亮度帧做自适应亮度拉伸,让行人轮廓更明显。经过两轮循环,夜间行人召回率从勉强及格提升到可接受水平。
5.3 量化后精度下降怎么定位
某次INT8量化后跑评估集,车辆和行人精度都正常,唯独红绿灯AP掉了五个点。我没有急着调校准集,而是先用FP16和INT8的engine各跑一遍相同的夜间样本,把两者输出差异最大的帧存下来,发现全部集中在红灯过曝场景。定位到问题后,把过曝样本加入校准集,同时把检测头附近几层保留FP16,红绿灯AP就回来了。这个排查过程说明,量化精度问题不能盲试参数,要先找到波形差异最大的数据切片,再做针对性处理。
5.4 延迟抖动与显存问题
部署上线后出现过一次端到端延迟偶发跳变,均值正常,但p95超过100毫秒。抓日志发现抖动发生在帧率波动时刻,以为是模型问题,排查下来是CPU端图像采集线程的写内存操作和推理线程抢内存带宽,导致GPU数据拷贝被拖慢。解决方案是使用锁页内存、给推理线程设置高优先级,并增加图像缓冲区的个数,让采集和推理之间具备更平滑的弹性,延迟抖动随之消失。
显存问题也值得单独提醒:TensorRT引擎加载、CUDA上下文、预处理缓冲、NMS中间结果会在运行时叠加占显存。上线前最好把每个模块的显存单独打点,量化出各环节的占用,而不是只看模型本身的大小。
5.5 排查速查表
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| 红绿灯误检 | 颜色分类和检测耦合 | 检测只出框,颜色交给状态机 |
| 远距离红灯漏检 | 分辨率不足、目标太小 | 增加ROI切图检测或P2头 |
| 夜间行人漏检 | 训练数据曝光不足 | 补充弱光数据、推理前亮度拉伸 |
| INT8后红灯AP下降 | 校准集缺过曝样本 | 校准集补夜间过曝、敏感层保留FP16 |
| p95延迟突刺 | CPU和GPU争抢内存带宽 | 锁页内存、线程优先级、多缓冲 |
| 显存OOM | 缓冲叠加超预算 | 逐模块打点显存、削减缓冲 |
| 长时间后性能下降 | 热降频 | 锁定时钟频率、优化风扇策略 |
最后再分享一个我个人的复盘经验:这个项目做到后期,我发现真正的瓶颈早就不是模型结构,而是工程边界的定义。精度和延迟永远是一对矛盾,关键是要提前想清楚哪些场景必须保、哪些场景可以妥协,然后把数据、训练、部署整条链路都围绕这个边界去做。YOLOv8只是工具,真正让它发挥作用的,是在每个环节里一点点抠出来的细节。比如量化校准集每一次随机抽样后要多跑几组对比,看看结果的方差大不大;测p95延迟时一定要先做足够多的warmup轮次再读数。这些小事积累起来,最终决定了一辆车跑在路上是稳定还是毛病不断。希望这篇实战记录能帮你少走几段弯路。