行业资讯
📅 2026/9/4 11:17:19
YOLOv8+TensorRT:农业机器人果蔬识别边缘部署全攻略
接到这个项目需求时我第一反应是农业机器人落地最难的部分往往不是算法本身而是怎么把算法塞进一个功耗有限、算力有限、还要在田里风吹日晒的嵌入式设备里。这篇文章就把我最近做的一个12类果蔬识别项目完整复盘一遍从数据集整理到YOLO模型训练再到Jetson上TensorRT加速部署整个链路里的选型思路、操作步骤、踩坑记录和最终实测数据都会写清楚。对于正在做边缘部署、或者准备把YOLO模型往Jetson上迁移的朋友这篇文章应该能帮你省掉不少弯路。先说项目背景一台在果园/大棚里巡检的农业机器人需要实时识别12类常见果蔬——苹果、香蕉、辣椒、黄瓜、番茄、草莓、橙子、土豆、胡萝卜、茄子、葡萄、西瓜。识别结果用来驱动机械臂采摘或产量估算。要求单帧处理延迟低于50ms约20FPS以上功耗控制在15W以内模型体积尽量小因为机器人主板上还要跑导航、避障等其他任务。这个需求决定了几个技术选型模型用YOLOv8ssmall版本硬件平台用NVIDIA Jetson Orin NX 16GB推理加速用TensorRT精度采用FP16量化。整套方案验证下来在640×640输入分辨率下能达到45~60FPS的稳定推理速度比纯PyTorch环境提升了接近5倍。1. 项目方案选型为什么农业机器人一定要走边缘部署1.1 12类果蔬识别的真实业务约束果蔬识别这个任务从算法角度看属于常规目标检测但农业场景有个和普通监控完全不同的约束网络环境。大棚、果园这些地方要么没有Wi-Fi要么信号差到视频回传都卡。如果依赖云端识别一次请求来回延迟动辄几百毫秒加上网络抖动机械臂根本没法实时的干活。另一个约束是功耗与散热。农业机器人通常用电池供电整机功耗预算非常紧张。我最初考虑过用笔记本级别的独立显卡做推理但一块RTX 4060的满载功耗就接近100W加上CPU、屏幕、外设整个机器人续航直接崩掉。Jetson平台的最大优势就是能以极低功耗提供可用的端侧算力——Orin NX 16GB模组的功耗范围是10W到25W可调算力最高100 TOPS稀疏实际跑模型时整机功耗能压在12W左右。还有数据隐私的问题。农场主的种植数据、产量数据属于商业机密不是所有人都愿意把视频流传到云端的。本地推理意味着原始图像不出设备录制的素材可以选帧抽稀后再回传做模型迭代但实时推理数据全部留在本地这在商务谈判时是个加分项。1.2 YOLO系列模型版本与尺寸的取舍果蔬识别这种任务目标形态差异大有的果蔬很小草莓、葡萄有的大西瓜、南瓜还有的密集排列一串葡萄里几十个果粒。但整体上目标纹理简单、背景相对单一叶子、土壤、大棚薄膜因此不需要超大模型。YOLOv8s和YOLOv8m我都训练过两者在验证集上的mAP50差距只有1.2个百分点m约96.5%s约95.3%但在Jetson Orin NX上的推理耗时差距却有近一倍。YOLOv8s在TensorRT FP16下大约16ms一帧YOLOv8m则需要28ms左右。考虑机器人还要并发跑避障模型我最终选择了YOLOv8s作为基线。如果换成YOLOv9c或YOLO11s推理速度和精度各有千秋但v8的生态最成熟部署资料最多遇到问题好查这个优势在项目排期紧张时非常关键。YOLOv8相比前代的最大变化是抛弃了anchor-based设计改成了anchor-free的检测头直接回归目标中心点和宽高。这意味着后处理流程更简单不需要做anchor匹配对TensorRT这类编译型优化器也更友好。另一个细节是v8的输出层包含了多个尺度的特征图P3/P4/P5转换engine时需要注意输出绑定的名称和shape后面部署部分我会详细说。1.3 TensorRT在Jetson上到底起了什么作用TensorRT是NVIDIA的推理优化引擎核心工作方式是在离线阶段把训练好的模型“编译”成针对特定GPU架构优化的engine文件。这个编译过程会做几件训练框架做不了的事层融合把ConvBatchNormReLU等合并成一个算子、kernel自动调优针对不同shape选最优实现、精度校准FP16/INT8量化、显存复用计算图分析后减少中间缓冲。我实测的一组数据很有参考价值同样是YOLOv8s模型640×640输入在Orin NX上用PyTorch原生推理耗时约78ms用ONNX RuntimeCUDA EP耗时约49ms用TensorRT FP16 engine耗时约16ms。这个速度提升不是靠硬件超频纯粹是软件栈优化带来的收益。TensorRT对卷积类网络的优化效果尤其明显因为这类网络的算子类型集中、计算图规整非常适合层融合。需要提醒的是TensorRT不是万能的。动态shape支持不友好INT8量化需要校准数据某些自定义算子比如部分注意力机制、特殊激活函数会提示不支持或触发fallback到性能很差的实现。所以模型设计阶段就要考虑部署兼容性尽量避免花哨的自定义结构。2. 果蔬数据集构建与训练细节mAP高不等于田间可用2.1 数据来源与标注规范果蔬识别在公开数据集上可选的其实不多。我主要用了三个来源Roboflow上公开的Fruit Detection数据集约5000张涵盖苹果、香蕉等常见果蔬、VisDrone中少量果园场景图像这部分需要重新筛选因为VisDrone主要是无人机视角低空航拍和地面机器人视角差异很大、以及自己标注的田间实拍图约3000张。这里有个非常关键的问题公开数据集里果蔬的形态、光照、成熟度分布和真实农场环境往往有差距。比如公开数据集里的番茄大多是红透的但大棚里的番茄经常是半红半绿的状态——训练时见过这种样本模型在真实场景里才不会漏检。我最终的策略是公开数据占60%自采数据占40%自采数据里特意覆盖了不同光照方向、不同成熟度、部分遮挡的样本。标注规范我踩过坑后总结了一套标准果实可见面积超过30%就标注不管遮挡同一根茎上的多个果实如果边缘重叠用两个框分别框住不要合并成一个框绿色果蔬黄瓜、青椒和叶片颜色接近时必须放大图片确认果实边界再标模糊样本直接弃用。标注工具用的是LabelImg导出YOLO格式的txt文件每行内容是类别id x_center y_center width height坐标值做了归一化。2.2 数据清洗与增强策略数据清洗这个环节经常被新手跳过但它对模型精度的贡献可能比调参还大。我用一段Python脚本自动检查了所有标注文件找出三类脏数据标注框面积小于图像面积0.5%的大概率是误标或无效小目标、宽高比超过1:8的果蔬目标不可能这么扁长、以及类别与数据集预设不符的比如叶子被标成了黄瓜。手工复核后清洗掉约400张脏数据标注准确率从92%提升到98%以上。数据增强方面YOLOv8训练默认开启Mosaic四张图拼接、HSV扰动、随机翻转等策略这些对果蔬任务很有效。我额外加了两个针对性的增强一是轻微的透视变换模拟机器人移动时视角变化二是Copy-Paste增强把单独标注的果实随机粘贴到背景图上增加果实密集分布的训练样本。这招对提高密集场景下的召回率帮助非常明显草莓和葡萄的mAP50分别提升了1.8%和2.3%。增强参数建议用YOLOv8默认值起步不要一开始就大改。默认的HSV扰动强度hsv_h0.015, hsv_s0.7, hsv_v0.4对果蔬颜色多样性其实已经覆盖得很好我试过加大饱和度扰动到1.0结果模型在真实低饱和环境下反而误检增多——因为训练集里出现了太多“过分鲜艳”的假样本。2.3 训练参数配置与精度验证训练环境是PC工作站NVIDIA RTX 4090PyTorch 2.0 Ultralytics 8.0.205。关键超参给出来供参考输入分辨率640×640batch_size 16epochs 200含50轮warmup优化器SGDmomentum0.937weight_decay0.0005初始学习率0.01采用cosine退火。Mosaic增强在前10个epoch关闭这是YOLOv8默认的行为目的是让模型先看到真实比例的目标避免从一开始就被拼接图干扰。训练过程大概耗时22小时final权重在自建验证集上的指标mAP5095.3%mAP50-9572.8%。水果类苹果、香蕉等AP50普遍在97%以上蔬菜类黄瓜、茄子稍低因为有些样本和叶子颜色太接近。最有说服力的是在陌生大棚环境里的实测视频截取2000帧做推理误检率4.2%漏检率1.7%多数误检发生在果实被叶子遮住70%以上的极端情况。这里想强调一个经验mAP是训练指标不是部署指标。mAP只衡量检测框和真实框的IoU匹配程度但它不能告诉你模型在正午强光下的表现、在高帧率视频里的稳定性、以及后处理参数NMS阈值、置信度阈值在实际场景中怎么调。一定要留出一批“没参与过训练”的真实场景视频做闭环测试甚至直接装在机器人上跑一天看录屏回放这才是唯一靠谱的验收方式。3. TensorRT转换链路从PyTorch权重到engine推理3.1 导出ONNX时的关键注意事项这一步是整个部署流程的中心环节。Ultralytics框架本身提供了export功能直接能导出ONNX但有几个参数必须手动确认yolo export modelbest.pt formatonnx opset12 dynamicTrue simplifyTrueopset版本建议固定为12过高版本在部分Jetson JetPack版本上兼容性有问题。dynamicTrue表示把batch维和输入输出shape设成动态的但TensorRT对动态shape支持不够灵活后面转engine时如果要固定batch1建议此处导出两个版本一个dynamic用于调试一个static用于正式部署。导出之后务必用onnxsim做一次静态图简化。这个工具会扔掉ONNX图里所有冗余的计算节点比如训练用的dropout节点、恒等映射节点同时做常量折叠。我用简化前后的onnx模型调试过简化后转换engine的失败率明显降低生成的engine推理速度也有约3%的提升。检查导出的ONNX是否正常可以用onnxruntime跑一遍对比输出。建议写个脚本随机给一张图分别用PyTorch和ONNX Runtime推理比较输出张量的余弦相似度正常情况下应该高于0.999。如果这里出现偏差先别急着转TensorRT因为问题在导出环节就已经出现了。最常见的原因是模型里有不支持的算子比如某些版本的自定义注意力模块导出时会被拆成细碎的计算节点精度损失明显。3.2 ONNX转TensorRT engine的命令行与Python API推荐用trtexec命令行工具做初步验证它最简单直观/usr/src/tensorrt/bin/trtexec --onnxyolov8s_12class.onnx \ --saveEnginebest_fp16.engine \ --fp16 \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 \ --maxShapesimages:8x3x640x640FP16模式是Jetson上性能和精度平衡的最佳选择。INT8理论上能再快30%左右但需要准备校准数据集而且对果蔬这类色彩鲜艳的目标量化误差容易被视觉感知我在第5章会详细展开。--fp16标志让TensorRT为支持的层选择FP16实现注意不是所有层都会转FP16某些精度敏感层如最后的sigmoid会自动保留FP32。正式项目里我推荐用Python API构建engine因为可以做更多自定义控制比如设置DLA深度学习加速器、控制工作空间大小、绑定动态shapeimport tensorrt as trt TRT_LOGGER trt.Logger(trt.Logger.WARNING) builder trt.Builder(TRT_LOGGER) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser trt.OnnxParser(network, TRT_LOGGER) with open(yolov8s_12class.onnx, rb) as f: parser.parse(f.read()) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 30) # 1GB config.set_flag(trt.BuilderFlag.FP16) # 固定batch1使用静态输入shape profile builder.create_optimization_profile() profile.set_shape(images, (1, 3, 640, 640), (1, 3, 640, 640), (1, 3, 640, 640)) config.add_optimization_profile(profile) engine builder.build_serialized_network(network, config) with open(best_fp16_static.engine, wb) as f: f.write(engine)转换成功的engine文件不要立刻部署先做个快速验证。用trtexec跑一下生成的enginetrtexec --loadEnginebest_fp16_static.engine --shapesimages:1x3x640x640观察输出的mean/median延迟确认性能符合预期。如果延迟异常高八成是某个算子fallback到了FP32实现需要回头看onnx图里是否卡了非融合的结构。3.3 engine推理代码框架从输入图像到检测结果engine文件无法直接读取你随手写的预处理逻辑需要自己实现完整的推理流程图像预处理resize、归一化、RGB通道转换、输入buffer拷贝、engine执行、输出buffer解析、NMS后处理。这里给一个可运行的最小框架import numpy as np import cv2 import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit class YOLOv8TensorRT: def __init__(self, engine_path, input_size(640, 640)): self.input_size input_size runtime trt.Runtime(trt.Logger(trt.Logger.WARNING)) with open(engine_path, rb) as f: self.engine runtime.deserialize_cuda_engine(f.read()) self.context self.engine.create_execution_context() # 分配输入输出buffer self.inputs, self.outputs, self.bindings [], [], [] for i in range(self.engine.num_bindings): name self.engine.get_binding_name(i) shape self.engine.get_binding_shape(i) size trt.volume(shape) * self.engine.max_batch_size dtype trt.nptype(self.engine.get_binding_dtype(i)) host_mem cuda.pagelocked_empty(size, dtype) device_mem cuda.mem_alloc(host_mem.nbytes) self.bindings.append(int(device_mem)) if self.engine.binding_is_input(i): self.inputs.append({name: name, host: host_mem, device: device_mem}) else: self.outputs.append({name: name, host: host_mem, device: device_mem}) def preprocess(self, img): h, w img.shape[:2] scale min(self.input_size[0] / h, self.input_size[1] / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(img, (new_w, new_h)) canvas np.full((self.input_size[0], self.input_size[1], 3), 114, dtypenp.float32) canvas[:new_h, :new_w] resized blob cv2.dnn.blobFromImage(canvas, 1/255.0, (self.input_size[1], self.input_size[0]), swapRBTrue, cropFalse) return blob, scale, new_w, new_h def infer(self, img): blob, scale, new_w, new_h self.preprocess(img) self.inputs[0][host] np.ascontiguousarray(blob.ravel()) cuda.memcpy_htod(self.inputs[0][device], self.inputs[0][host]) self.context.execute_v2(bindingsself.bindings) cuda.memcpy_dtoh(self.outputs[0][host], self.outputs[0][device]) return self.outputs[0][host], scale, new_w, new_h这里有个容易忽略的细节TensorRT的输出是原始检测头张量不是已经过滤好的检测框。YOLOv8的输出shape是1×84×8400以COCO 80类为例12类则是1×16×8400其中84 4个框坐标 80个类别概率12类则是16 4 128400是三个尺度特征图的anchor点总数。你需要自己实现解码先按阈值过滤低置信度框再做NMS去重最后把归一化坐标映射回原图尺寸。后处理这部分的代码网上有很多开源参考核心注意点是类别概率要用sigmoid激活YOLOv8输出的是logits框坐标是中心点宽高而不是角点坐标。这些细节如果搞错会出现“engine正常但检测结果全是乱框”的诡异问题。4. Jetson上部署环境配置烧录、版本匹配与性能调优4.1 设备选型与系统烧录Jetson产品线里Orin NX 16GB和Orin Nano 8GB是当前最热门的两个选择。两者都能跑YOLOv8s TensorRT但定位不同Orin NX 16GB有100 TOPS算力稀疏支持更大的模型和更高的分辨率官方建议零售价约$499Orin Nano 8GB只有40 TOPS价格约$249适合原型验证和轻量应用。如果你需要在端侧跑YOLOv8s 避障模型 导航模型三个并发任务Orin NX是底线。如果只需要跑一个目标检测模型Orin Nano完全够用。系统烧录分两种方式。最简单的是用SDK Manager需要NVIDIA账号在Ubuntu主机上运行它会自动下载JetPack镜像并写入开发套件的eMMC或NVMe SSD。另一种是手动烧录到NVIDIA官网下载对应设备型号的Jetson Linux驱动包用./flash.sh脚本写入。开发套件连接显示器、鼠标和键盘很简单Orin NX开发套件背面有一个HDMI接口和多个USB口直接接上就行如果用的是生产模组载板需要确认载板是否预留了显示接口和USB Host接口有些工业载板为了省电会把显示接口裁掉只能通过串口或网络SSH访问这个选型前一定要确认清楚。烧录完成后建议立刻做三件事更新系统软件包、安装中文输入法需要的字库边写文档边用、以及开启SSH服务方便远程调试。Jetson默认用户名是nvidia密码nvidia首次登录会强制改密码。4.2 CUDA、cuDNN、TensorRT版本对应关系Jetson的软件生态和PC很不一样。PC上你可以随便装最新版CUDA但Jetson上所有深度学习组件都和JetPack版本绑定。比如JetPack 5.1.2对应CUDA 11.4、cuDNN 8.6、TensorRT 8.5.2JetPack 5.1.3对应CUDA 11.4、TensorRT 8.6.2。想在Jetson上手动升级CUDA是很痛苦的事情因为NVIDIA自己编译的PyTorch、TensorRT都是基于特定JetPack版本做的二进制兼容。我的建议是锁定一个JetPack版本用到项目结束不要中途升级。项目开始前先到NVIDIA官网的“JetPack Archive”页面查好各版本对应的组件版本再根据你的PyTorch版本需求来选。比如你训练环境用的是PyTorch 2.0那JetPack 5.1.2之后都支持因为NVIDIA提供了对应的PyTorch wheelpytorch 2.0.0nv23.05。检查当前环境版本用的命令# 检查JetPack版本 cat /etc/nv_tegra_release # 检查TensorRT版本 dpkg -l | grep TensorRT # 或Python侧检查 python3 -c import tensorrt; print(tensorrt.__version__) # 检查CUDA nvcc --version版本匹配是一切部署的基础。我看过太多人在Jetson上折腾了一周最后发现是cuDNN和TensorRT版本不匹配导致引擎构建失败。4.3 电源模式、风扇控制与性能墙处理Jetson默认的运行模式是6MAXN会跑满性能但功耗高。对农业机器人这种电池供电场景建议使用模式115W或模式225W实测在模式1下YOLOv8s TensorRT推理也能跑到40FPS左右对功耗敏感的项目完全够用。# 查看当前电源模式 sudo nvpmodel -q # 切换到15W模式 sudo nvpmodel -m 1 # 开启风扇部分开发套件需要手动开 echo 255 | sudo tee /sys/devices/pwm-fan/target_pwm这里有个实际测试数据Orin NX开发套件在满负载运行FP16推理时裸板散热片的温度能到82℃左右摸上去烫手。如果机器人机箱内通风不好建议加装一个5V主动散热风扇温度能降到65℃以下性能也能保持稳定——Jetson有温度墙机制核心温度超过85℃会主动降频导致推理延迟波动这对机器人控制是大忌。实际部署时还有一个容易忽略的点Jetson开发套件和量产模组在散热设计上差别很大。开发套件有大面积散热片量产模组通常需要自己设计散热方案铝块导热硅脂风扇。如果你的项目要做产品化散热结构一定要尽早测试不要等软件全跑通了才补散热到时候可能因为过热频繁降频而需要重新调模型就非常难受了。5. 边缘部署踩坑实录精度、速度与稳定性的平衡5.1 INT8量化丢精度一次完整的排查过程项目初期为了追求极致速度我尝试了INT8量化。TensorRT的INT8量化需要提供一个校准数据集用它来统计每层激活值的分布从而确定浮点到定点的映射关系。我的第一次校准数据集是从训练集里抽取了500张图转换后engine推理速度确实达到了9ms非常快但mAP50直接从95.3%掉到了88.7%漏检率翻了一倍。排查过程是这样的先逐类分析精度变化发现红色果蔬苹果、番茄、草莓掉点最严重绿色果蔬黄瓜、青椒掉点轻一些。进一步对比INT8 engine和FP16 engine在相同图片上的输出差异发现高置信度目标的前景区域概率值平均下降了0.08左右同时NMS阈值附近的弱目标更容易被过滤掉。最终定位原因是校准数据集分布和实际部署场景偏离过大训练集里果蔬占画面中心、背景简洁而部署场景是整棵果树/整片藤蔓的复杂画面背景区域大、果蔬目标小且多。校准数据里没有充分覆盖这种小目标复杂背景的分布导致量化参数偏差。解决方案分两步一是把校准集补充到800张均衡覆盖每类果蔬、不同光照、不同尺度二是对校准过程做“数据增强后再校准”让模型见过更多灰度分布。优化后的INT8 engine mAP50恢复到93.1%虽然仍比FP16低2个百分点但换来了近40%的速度提升在目标较少、单一果品种植园的场景下是够用的。我的建议如果检测目标是比较“标准”的物体如交通标志、工业零件INT8量化通常问题不大。如果目标颜色鲜艳多变果蔬、非常依赖颜色纹理细节优先用FP16只有算力确实吃紧时再考虑INT8并务必准备充足且贴合场景的校准数据。5.2 显存不足与长时运行稳定性问题Orin NX 16GB的显存其实是和内存共用的统一内存架构实际GPU可用约12GB左右所以不用担心CUDA out of memory这种PC上常见问题。但Jetson上频繁踩的坑是多个进程共用GPU资源时某个进程的显存泄露会导致整机OOM。我在跑机器人整机联调时就遇到过视觉进程推理12小时后显存从1.2GB涨到6.3GB最终导致导航进程崩溃。排查显存泄露的方法是用tegrastats工具监控GPU内存占用然后对推理进程做长时间压力测试至少12小时。如果占用持续上涨基本可以断定是某个环节没有释放缓冲。在pycuda里最容易出错的就是没有调用cuda.memcpy_dtoh之后释放device指针。建议推理类进程每处理1000帧输出一次内部buffer状态从外部脚本定期监控进程内存出现问题能第一时间定位。另一个稳定性问题是Jetson长时间工作后温度上升导致的降频。除了加散热外我还在代码里加了“推理频率自适应”逻辑根据最近100帧的平均推理延迟动态调节每帧之间的sleep时间避免无意义的空转发热。这个技巧简单有效把长时间运行的平均功耗降低了约15%。5.3 视频流优化从OpenCV到GStreamer用OpenCV的VideoCapture(rtsp://...)读摄像头流是大多数人的第一直觉但在Jetson上这个方案CPU占用偏高解码1080p30的H.264流时CPU占用率能到40%以上严重挤压了推理的资源预算。原因是OpenCV用的是FFmpeg软解没有利用Jetson的硬件视频解码器NVDEC。正确做法是用GStreamer插件调用NVDEC硬解再把解码后的BGR帧直接送到推理模块。一个通用的管道配置gst-launch-1.0 rtspsrc locationrtsp://192.168.1.200:8554/video1 \ ! rtph264depay ! h264parse ! nvv4l2decoder \ ! nvvidconv ! video/x-raw,formatBGRx \ ! videoconvert ! video/x-raw,formatBGR \ ! appsink dropTrue实测CPU占用从40%降到8%而且解码延迟更低、更稳定。如果摄像头直接输出RAW RGB比如USB摄像头用v4l2srcnvvidconv做硬件格式转换即可。Jetson上有现成的Python绑定cv2.VideoCapture(appsrc ! ... , cv2.CAP_GSTREAMER)但参数写起来容易出错。建议直接在C或Python侧调用gst-python的appsrc/appsink接口错误信息更直观、调试更方便。6. 开发机与部署机的差异化管理关于显卡选型的现实建议6.1 AMD显卡能跑YOLO吗ROCm与CUDA生态的现实这个问题的讨论度很高我看到很多人在问“AMD RX 580能不能跑YOLOv8”。直接给结论理论可以实际体验劝退。YOLOv8的训练和推理底层依赖PyTorchPyTorch要想用上AMD显卡需要ROCm支持。ROCm是AMD的CUDA替代方案但目前对桌面级显卡的官方支持列表很窄RX 580这种老架构卡压根不在官方支持列表里。社区有通过编译特定版本ROCm跑起来的案例但性能和稳定性都远不如同价位N卡的体验。如果手头只有AMD显卡又必须深度学习实际可行的选择是模型训练用CPUYOLOv8s在高端CPU上训练虽然慢但能忍batch减到4一个epoch约15分钟或者直接用云端GPUGoogle Colab免费版就能跑付费版更稳本地只做推理部署到Jetson上。另一个思路是用yolov8n这种更小的模型在CPU上做实时推理帧率虽然只有个位数但可以做离线批量检测。这里我的建议很务实如果你打算长期做AI落地项目训练阶段坚持用NVIDIA显卡是省时省力的最佳路径。RTX 3060 12GB就能舒服地训练YOLOv8s二手价格也合理。AI应用开发的时间成本远高于显卡差价别在显卡生态上省这几千块钱。6.2 开发机与部署机的环境分层策略我在这个项目里用了一套环境分层策略让开发和部署切换非常丝滑。开发机PC工作站上专门建了一个conda环境锁定PyTorch 2.0 CUDA 11.8 Ultralytics 8.0.205只负责训练和导出ONNX。导出ONNX后立即用ONNX Runtime在PC上验证精度这一步通过后把ONNX文件拷到Jetson上用TensorRT做“最后一公里”优化。这样做的意义在于开发机和部署机的软件栈差异被“ONNX格式”给切断了。开发机上折腾CUDA版本不会影响JetsonJetson只要保证TensorRT版本与ONNX算子兼容就行。部署脚本我写成了一个srun.sh启动时自动检查Python依赖、验证engine文件和输入图像是否存在省去了手工重复配置的麻烦。#!/bin/bash # 一键部署脚本核心逻辑 python3 -c import tensorrt, torch; assert tensorrt.__version__.startswith(8.5), TensorRT版本不对 # 如果engine不存在自动从onnx构建 if [ ! -f best_fp16.engine ]; then python3 build_engine.py --onnx yolov8s_12class.onnx --output best_fp16.engine fi # 启动推理服务 python3 run_inference.py --engine best_fp16.engine --input rtsp://localhost:8554/camera --port 5000这个脚本在项目交接时特别值钱因为调试时手工敲过的每条命令都被固化下来了新同事接手部署只需要跑一次脚本。6.3 从单一模型到多模型流水线的扩展思路农业机器人通常不会只跑一个模型。我目前的架构是主控进程JetPack自带的L4T负责调度视觉进程独立跑YOLOv8s果蔬识别导航进程跑一个轻量的YOLOv5n用于行人/障碍物检测两个进程共享GPU但错峰推理——视觉模型每帧推理约16ms导航模型每帧约8ms交替执行时GPU利用率接近满负荷但不会互相阻塞。如果后续要加果实分割比如用YOLOv8-seg输出每个果实的精细mask计算量会明显上升。实测YOLOv8s-seg在Orin NX上FP16 engine大约28ms一帧配合TensorRT的内存池复用整体流水线延迟仍能控制在50ms以内。另一个可扩展的方向是果实计数和定位把检测输出的2D框通过机器人自身的深度摄像头如Intel RealSense D435转成3D坐标再根据深度图做果实重心聚类配合机械臂的逆运动学完成抓取。这个扩展在架构上不需要改动推理部分只需要在后处理里加一个深度映射模块。7. 一些实测数据和经验体会最后把项目最终形态的实测数据整理一下。使用的是Jetson Orin NX 16GB开发套件JetPack 5.1.2TensorRT 8.5.2 FP16 engine输入640×640YOLOv8s-12类模型。配置项数值模型权重大小22.5MBengine文件大小43.1MB平均推理延迟16.2ms峰值推理延迟21.8ms平均功耗15.8W启动至就绪时间3.2s连续运行24小时帧数约96万帧GPU内存无泄漏实测总帧率含预处理/后处理48FPS从项目启动到完全落地实际花费的人力时间大约四天数据准备和训练一天半TensorRT转换和验证半天Jetson环境搭建和部署脚本一天现场实测和调参一天。最花时间的不是TensorRT本身而是调试engine推理时的边界情况——比如不同分辨率输入的坐标映射、输出buffer对齐、NMS阈值在真实场景下的微调。关于部署地点的经验我想说的是果蔬识别领域的公开模型和预训练权重很多但真正阻碍落地的是“从通用模型到现场可用”的最后一公里。公开数据集上mAP再高到了具体的大棚、具体的品种、具体的光照条件下大概率需要重新采集数据微调同时要做一次针对性的部署优化。这个认知越早建立项目排期就越准确。如果让我再做一个类似项目我会在训练阶段就把TensorRT的算子兼容性考虑进去比如提前确认检测头的layer结构不要引入特殊op。另外会从一开始就用GStreamer管道做视频采集而不是先凑合OpenCV后期再改。这些细节虽然不起眼但能避免后期推翻重来的尴尬。这次项目的完整代码和部署脚本我已经整理好了包括数据清洗脚本、训练配置、ONNX导出命令、TensorRT构建脚本、推理demo需要的朋友可以在此基础上改改类别数就能复用。有疑问的欢迎一起交流特别是显存优化和多模型调度方面我知道还有些更好的做法值得继续挖。