Jetson Nano部署YOLOv4口罩检测:从模型转换到TensorRT优化的完整实践
1. 项目概述边缘计算下的口罩检测方案最近几年边缘计算设备在安防、工业质检、智慧零售等领域越来越火其中Nvidia的Jetson系列开发板凭借其强大的GPU算力和紧凑的功耗成为了很多嵌入式AI项目的首选平台。今天我想和大家分享一个我前段时间在Jetson Nano上折腾的实战项目一个基于YOLOv4的实时口罩佩戴检测器。这个项目听起来可能不新鲜但真正从零开始在资源受限的边缘设备上跑通一个实时、高精度的检测模型中间踩的坑、做的优化才是最有价值的干货。简单来说这个项目的目标就是让Jetson Nano这块巴掌大的开发板能够实时处理摄像头视频流准确识别画面中的人脸并判断其是否规范佩戴了口罩。它非常适合部署在商场入口、办公楼大堂、公共交通枢纽等需要快速筛查的公共场所作为一道非接触式的自动化防疫辅助。选择YOLOv4是因为它在精度和速度之间取得了很好的平衡相比更早的版本其网络结构和训练技巧如Mosaic数据增强、CIoU损失函数让它在复杂场景下的表现更稳健。而Jetson Nano作为入门级边缘AI设备其128核Maxwell架构GPU和四核ARM CPU正好为运行这类经过适当优化的中等规模目标检测模型提供了可能。如果你是一名嵌入式开发者、计算机视觉爱好者或者正在寻找低成本、可离线运行的智能视觉解决方案那么跟着我走一遍这个项目的完整实现路径从环境配置、模型转换、代码编写到性能优化你应该能获得一套可以直接复用的方法论。整个过程不依赖于复杂的云服务所有计算都在本地完成确保了数据隐私和系统响应的实时性。2. 核心思路与方案选型背后的考量为什么是Jetson Nano YOLOv4这个组合这背后是一系列针对边缘场景的权衡。首先我们需要一个足够轻量且高效的模型。YOLOv3是一个经典选择但YOLOv4在保持相近速度的前提下通过引入CSPDarknet53骨干网络、PANet路径聚合和SPP模块显著提升了检测精度特别是对小目标和遮挡目标的检测能力。对于口罩检测这个任务人脸可能侧对镜头、部分遮挡YOLOv4的这些特性正好派上用场。其次硬件平台的选择决定了项目的天花板。Jetson Nano虽然算力有限约472 GFLOPS FP16但其完整的CUDA和TensorRT支持生态是无可比拟的优势。这意味着我们可以利用Nvidia强大的推理优化引擎——TensorRT将训练好的模型转换成高度优化的引擎文件从而榨干硬件每一分性能。相比之下在其他ARM开发板如树莓派上直接运行原生PyTorch或TensorFlow模型帧率可能惨不忍睹。整个技术栈的构建思路非常清晰在性能强大的服务器或PC上使用PyTorch或Darknet框架训练一个针对口罩检测任务优化过的YOLOv4模型。然后将这个模型通过ONNX作为中间格式最终转换为TensorRT引擎部署到Jetson Nano上。在Nano上我们使用OpenCV捕获视频流用TensorRT运行时加载引擎进行推理最后将检测结果边界框、类别、置信度绘制到图像上并显示或推流。这个流程分离了训练和部署环境让边缘设备只做它最擅长的事高效推理。注意模型训练需要大量的标注数据和GPU算力这通常在云端或本地高性能工作站上完成。本文重点在于部署和优化环节训练部分会简要带过关键数据准备和训练技巧。3. Jetson Nano开发环境深度配置工欲善其事必先利其器。在Jetson Nano上搭建一个稳定且高效的环境是项目成功的第一步。Nano出厂通常预装了JetPack SDK这是一个包含了Ubuntu系统、CUDA、cuDNN、TensorRT、OpenCV等核心组件的软件包。首先确认你的JetPack版本。我使用的是JetPack 4.6对应CUDA 10.2 TensorRT 8.0 以及OpenCV 4.1.1。你可以通过命令nvcc -V和dpkg -l | grep tensorrt来查看具体版本。3.1 系统基础优化与依赖安装刚刷好的系统需要做一些优化以提升性能。首先调整Nano的运行模式。Jetson Nano有两种电源模式5W和10WMax-N。对于需要持续进行AI推理的项目强烈建议使用10W模式以获得全部算力。可以通过命令sudo nvpmodel -m 0设置为Max-N模式10W并使用sudo jetson_clocks让CPU和GPU运行在最高频率。接下来是安装Python环境。我推荐使用系统自带的Python 3.6并为其创建虚拟环境避免污染系统Python。sudo apt update sudo apt install python3-pip python3-dev python3-venv cd ~ python3 -m venv maskenv source maskenv/bin/activate激活虚拟环境后安装一些基础的科学计算和图像处理库。由于Nano是ARM架构很多预编译的wheel包不兼容需要从源码编译这非常耗时。一个技巧是使用Nvidia官方提供的或者社区维护的针对JetPack版本预编译的wheel包。例如对于PyTorch可以去Nvidia论坛或PyTorch官网查找对应JetPack 4.6的版本。# 示例安装预编译的PyTorch (版本需对应你的JetPack) wget https://nvidia.box.com/shared/static/xxxxxxxxxx.whl -O torch-1.10.0-cp36-cp36m-linux_aarch64.whl pip3 install torch-1.10.0-cp36-cp36m-linux_aarch64.whl然后安装其他必要的包部分可能需要编译pip3 install numpy cython # 安装matplotlib等可能需要额外系统库 sudo apt install libfreetype6-dev pip3 install matplotlib3.2 OpenCV with CUDA加速编译指南系统预装的OpenCV通常不支持GPU加速这对于视频处理是巨大的性能损失。为了最大化利用Jetson Nano从源码编译一个开启CUDA后端的OpenCV是至关重要的一步。这个过程大约需要1-2小时。首先安装大量的编译依赖项sudo apt install build-essential cmake git unzip pkg-config sudo apt install libjpeg-dev libpng-dev libtiff-dev sudo apt install libavcodec-dev libavformat-dev libswscale-dev sudo apt install libgtk-3-dev libcanberra-gtk3* sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev sudo apt install libxvidcore-dev x264 libx264-dev sudo apt install python3-dev python3-numpy sudo apt install libtbb2 libtbb-dev libdc1394-22-dev sudo apt install libv4l-dev v4l-utils sudo apt install libopenblas-dev libatlas-base-dev libblas-dev sudo apt install liblapack-dev gfortran libhdf5-dev sudo apt install libprotobuf-dev protobuf-compiler sudo apt install libgoogle-glog-dev libgflags-dev sudo apt install libavresample-dev下载OpenCV源码这里以4.5.5为例建议选择与TensorRT兼容较好的版本cd ~ wget -O opencv.zip https://github.com/opencv/opencv/archive/4.5.5.zip wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.5.5.zip unzip opencv.zip unzip opencv_contrib.zip配置CMake关键是要开启CUDA、CUDNN并为Python3绑定构建cd ~/opencv-4.5.5 mkdir build cd build cmake -D CMAKE_BUILD_TYPERELEASE \ -D CMAKE_INSTALL_PREFIX/usr/local \ -D WITH_CUDAON \ -D CUDA_ARCH_BIN5.3 \ -D CUDA_ARCH_PTX \ -D WITH_CUDNNON \ -D OPENCV_DNN_CUDAON \ -D ENABLE_FAST_MATHON \ -D CUDA_FAST_MATHON \ -D WITH_CUBLASON \ -D OPENCV_EXTRA_MODULES_PATH~/opencv_contrib-4.5.5/modules \ -D HAVE_opencv_python3ON \ -D BUILD_opencv_python3ON \ -D PYTHON3_EXECUTABLE/usr/bin/python3 \ -D PYTHON3_INCLUDE_DIR/usr/include/python3.6m \ -D PYTHON3_LIBRARY/usr/lib/aarch64-linux-gnu/libpython3.6m.so \ -D PYTHON3_NUMPY_INCLUDE_DIRS/usr/lib/python3/dist-packages/numpy/core/include \ -D BUILD_EXAMPLESOFF \ -D BUILD_opencv_javaOFF \ -D BUILD_TESTSOFF \ -D BUILD_PERF_TESTSOFF ..提示CUDA_ARCH_BIN5.3是针对Jetson Nano的Maxwell架构GPU的计算能力版本号必须设置正确。OPENCV_DNN_CUDAON允许OpenCV的DNN模块使用CUDA进行推理这对后续使用OpenCV直接运行某些模型有加速作用。配置完成后开始编译和安装。使用make -j4进行编译4核并行安装需要sudo权限。make -j4 sudo make install sudo ldconfig编译完成后在Python中验证OpenCV版本及CUDA支持import cv2 print(cv2.__version__) print(cv2.cuda.getCudaEnabledDeviceCount()) # 应该输出大于04. YOLOv4模型训练与转换关键步骤4.1 数据准备与标注规范任何检测模型的上限都取决于数据质量。对于口罩检测我们需要收集包含各种场景室内、室外、不同光照、各种人脸姿态正面、侧面、低头、以及不同口罩类型和佩戴方式规范、不规范、未佩戴的图片。数据可以从公开数据集如MAFA Moxa3K获取也需要自己采集一部分以贴合实际部署环境。标注工具推荐使用LabelImg或更高效的CVAT。标注时只框出人脸区域从额头到下颚两侧到耳朵并为每个边界框指定两类标签with_mask规范佩戴和without_mask未佩戴或不规范如露出鼻子。这里有一个关键细节对于佩戴不规范的情况统一归为without_mask。因为我们的目标是筛查风险宁可严格一些。标注文件保存为YOLO格式每个图片对应一个.txt文件内容如0 0.5 0.5 0.3 0.4分别代表类别id、中心点x、y、宽度、高度均为归一化坐标。数据划分建议按7:2:1分为训练集、验证集和测试集。为了增强模型的鲁棒性必须进行数据增强。YOLOv4原生支持Mosaic增强将四张图片拼成一张和MixUp增强这些在训练配置中开启即可。此外还可以在预处理中加入随机色彩抖动、模糊、旋转等。4.2 模型训练与关键参数调优我选择使用Darknet框架来训练YOLOv4因为它原生支持且效率高。首先在带GPU的训练服务器上克隆Darknet仓库并编译开启GPU和OpenCV。git clone https://github.com/AlexeyAB/darknet.git cd darknet # 修改Makefile设置GPU1, CUDNN1, OPENCV1 make接下来是配置文件准备。需要三个关键文件模型配置文件 (yolov4-mask.cfg)基于yolov4.cfg修改。主要改动width和height根据Nano的输入分辨率调整我使用416x416以平衡速度和精度max_batches设置为classes*2000这里2类设为4000steps设置为max_batches的80%和90%即32003600将三个yolo层和其前的convolutional层中的classes参数从80改为2将这三个convolutional层中的filters参数从255改为(classes 5) * 3即21。数据配置文件 (mask.data)classes2 train /path/to/train.txt valid /path/to/val.txt names /path/to/mask.names backup /path/to/backup/类别名称文件 (mask.names)with_mask without_masktrain.txt和val.txt是包含所有训练/验证图片绝对路径的文本文件。下载YOLOv4的预训练权重yolov4.conv.137作为迁移学习的起点可以加速收敛。开始训练./darknet detector train mask.data yolov4-mask.cfg yolov4.conv.137 -dont_show -map训练过程可以通过-map选项实时观察mAP的变化。当平均损失avg loss不再显著下降且验证集mAP达到满意水平例如在测试集上90%时即可停止训练。最终得到的yolov4-mask_best.weights就是我们的训练成果。4.3 模型格式转换从Darknet到TensorRT在Jetson Nano上直接运行Darknet模型效率不高。我们需要将其转换为TensorRT引擎。转换路径通常是Darknet (.weights) - ONNX (.onnx) - TensorRT (.engine)。ONNX作为一个开放的模型交换格式起到了桥梁作用。首先将Darknet模型转换为ONNX。可以使用https://github.com/Tianxiaomo/pytorch-YOLOv4这个仓库中的转换脚本。大致步骤是在训练服务器上用PyTorch加载我们训练好的.weights和.cfg文件然后使用PyTorch的ONNX导出功能。这里需要特别注意输出节点的名称和维度要与你后续TensorRT推理代码中的输入输出对应。得到.onnx文件后将其拷贝到Jetson Nano上。接下来在Nano上使用TensorRT的trtexec工具或编写Python转换脚本进行转换。trtexec是命令行工具比较方便/usr/src/tensorrt/bin/trtexec --onnxyolov4-mask.onnx --saveEngineyolov4-mask.engine --fp16 --workspace1024关键参数解析--fp16: 启用FP16半精度浮点数模式。这是Jetson Nano上提升推理速度的关键能带来近一倍的性能提升而精度损失对于目标检测任务通常可以接受。--workspace1024: 设置GPU内存工作空间为1024MB。TensorRT在优化网络时会尝试不同的内核实现需要临时内存。如果转换失败提示内存不足可以适当调低此值如512。你还可以添加--inputIOFormatsfp16:chw --outputIOFormatsfp16:chw来指定输入输出也为FP16格式进一步减少数据传输量。转换成功后会生成yolov4-mask.engine文件。这个文件是硬件和TensorRT版本相关的换一台不同型号的Jetson或者升级了TensorRT版本后可能需要重新转换。5. TensorRT推理引擎的部署与代码实现有了优化后的引擎文件接下来就是在Jetson Nano上编写推理程序。我们将使用TensorRT的Python API来加载引擎并用OpenCV处理视频流。5.1 TensorRT推理代码结构解析首先安装TensorRT的Python包。它通常随JetPack安装在系统目录我们需要找到其路径并链接到虚拟环境中。# 找到TensorRT Python包的路径通常在 /usr/lib/python3.6/dist-packages/ cp -r /usr/lib/python3.6/dist-packages/tensorrt* ~/maskenv/lib/python3.6/site-packages/ # 还需要pycuda用于内存管理和流同步 pip3 install pycuda核心推理类TrtYOLOv4的初始化部分需要完成以下工作加载引擎文件读取.engine文件到缓冲区。创建运行时Runtime和引擎Engine通过trt.Runtime反序列化引擎。创建执行上下文ExecutionContext这是实际执行推理的接口。分配输入输出内存在GPU上为输入和输出张量分配显存并在主机CPU端创建对应的缓冲区。YOLOv4的输入通常是1x3x416x416的FP16或FP32数据。创建CUDA流用于管理异步的GPU操作顺序。以下是关键代码片段的结构import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit import cv2 import numpy as np class TrtYOLOv4: def __init__(self, engine_path): # 1. 加载引擎 with open(engine_path, rb) as f: engine_data f.read() runtime trt.Runtime(trt.Logger(trt.Logger.WARNING)) self.engine runtime.deserialize_cuda_engine(engine_data) self.context self.engine.create_execution_context() # 2. 分配内存 self.inputs, self.outputs, self.bindings, self.stream self.allocate_buffers() # 3. 后处理参数锚点、类别数等 self.num_classes 2 self.anchors [[...], [...], [...]] # YOLOv4的锚点 self.strides [8, 16, 32] def allocate_buffers(self): # 遍历引擎的所有绑定输入输出分配GPU和CPU内存 inputs, outputs, bindings [], [], [] stream cuda.Stream() for binding in self.engine: size trt.volume(self.engine.get_binding_shape(binding)) # 计算体积 dtype trt.nptype(self.engine.get_binding_dtype(binding)) # 在GPU上分配内存 gpu_mem cuda.mem_alloc(size * dtype.itemsize) bindings.append(int(gpu_mem)) # 在CPU上分配内存 cpu_mem np.empty(size, dtypedtype) if self.engine.binding_is_input(binding): inputs.append({mem: gpu_mem, cpu: cpu_mem, shape: self.engine.get_binding_shape(binding)}) else: outputs.append({mem: gpu_mem, cpu: cpu_mem, shape: self.engine.get_binding_shape(binding)}) return inputs, outputs, bindings, stream def infer(self, image): # 预处理调整大小、归一化、HWC转CHW、添加批次维度 input_img self.preprocess(image) # 将CPU数据拷贝到GPU输入内存 np.copyto(self.inputs[0][cpu], input_img.ravel()) cuda.memcpy_htod_async(self.inputs[0][mem], self.inputs[0][cpu], self.stream) # 执行推理 self.context.execute_async_v2(bindingsself.bindings, stream_handleself.stream.handle) # 将GPU输出拷贝到CPU for out in self.outputs: cuda.memcpy_dtoh_async(out[cpu], out[mem], self.stream) self.stream.synchronize() # 等待流中所有操作完成 # 获取所有输出数据 outputs [out[cpu].reshape(out[shape]) for out in self.outputs] # 后处理解码边界框应用NMS detections self.postprocess(outputs, image.shape) return detections def preprocess(self, img): # 调整到网络输入尺寸如416x416 img_resized cv2.resize(img, (self.input_w, self.input_h)) # 归一化到0-1或使用YOLO的/255.0 img_normalized img_resized.astype(np.float32) / 255.0 # HWC to CHW img_chw np.transpose(img_normalized, (2, 0, 1)) # 添加批次维度 img_batched np.expand_dims(img_chw, axis0) # 如果转换时用了--inputIOFormatsfp16:chw这里需要转换为np.float16 # img_batched img_batched.astype(np.float16) return img_batched def postprocess(self, outputs, orig_shape): # 这是YOLO解码的核心部分将三个尺度的输出张量解码为边界框、置信度和类别概率。 # 1. 遍历三个输出层对应三个尺度 # 2. 将预测的tx,ty,tw,th根据锚点和网格位置解码为实际的bx,by,bw,bh相对于416x416 # 3. 应用sigmoid函数到置信度和类别分数 # 4. 根据置信度阈值如0.5和类别概率阈值如0.5进行初步筛选 # 5. 将边界框坐标从416x416尺度缩放到原始图像尺度 # 6. 应用非极大值抑制NMS去除重叠框 # 返回格式[x1, y1, x2, y2, confidence, class_id] passpostprocess函数是YOLO解码的核心代码较长。其逻辑是TensorRT引擎的输出通常是三个张量对应YOLOv4的三个检测头大、中、小目标。每个张量的形状为[1, (5num_classes)*3, grid_h, grid_w]。你需要遍历每个网格grid cell和每个锚点anchor计算边界框的实际坐标和尺寸然后应用sigmoid函数得到置信度和类别概率最后进行阈值筛选和NMS。5.2 视频流处理与结果可视化主循环主程序负责串联起视频捕获、推理、绘制和显示。def main(): # 初始化TensorRT推理引擎 trt_detector TrtYOLOv4(‘yolov4-mask.engine’) # 打开摄像头或视频文件 cap cv2.VideoCapture(0) # 0为默认摄像头或替换为视频路径 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) if not cap.isOpened(): print(“无法打开视频源”) return print(“开始检测按 ‘q’ 键退出...”) while True: ret, frame cap.read() if not ret: break # 记录推理开始时间 start_time time.time() # 执行推理 detections trt_detector.infer(frame) # 记录推理结束时间 inference_time time.time() - start_time fps 1.0 / inference_time # 在图像上绘制结果 for det in detections: x1, y1, x2, y2, conf, cls_id det label f{‘戴口罩’ if cls_id 0 else ‘未戴口罩’} {conf:.2f} color (0, 255, 0) if cls_id 0 else (0, 0, 255) # 绿色戴红色未戴 cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) cv2.putText(frame, label, (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # 显示FPS cv2.putText(frame, f“FPS: {fps:.1f}”, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2) cv2.imshow(‘Face Mask Detection’, frame) if cv2.waitKey(1) 0xFF ord(‘q’): break cap.release() cv2.destroyAllWindows()这个主循环清晰展示了从捕获帧到显示结果的完整流程。绘制结果时我们用绿色框表示“戴口罩”红色框表示“未戴口罩”并在框上方显示类别和置信度。屏幕左上角实时显示推理FPS这是评估性能最直观的指标。6. 性能优化与调优实战记录在Jetson Nano上不经过优化原始的YOLOv4模型可能只能跑到2-3 FPS完全无法满足实时性要求。我们的优化目标是达到10 FPS以上。以下是我在实践中验证有效的几种方法6.1 模型与输入分辨率权衡这是最有效的杠杆。YOLOv4的默认输入是608x608但对于边缘设备来说太大了。我测试了不同分辨率下的精度和速度608x608: mAP最高约94%但速度仅~2 FPS。内存占用大。416x416: mAP轻微下降约92%速度提升至~5-6 FPS。这是精度和速度的一个较好平衡点。320x320: mAP下降较明显约88%速度可达~10 FPS。适用于对速度要求极高、场景相对简单的环境。256x256: 速度更快~15 FPS但小目标漏检严重不推荐。建议在Jetson Nano上416x416是首选。如果场景中人脸都比较大且清晰可以尝试320x320以获得更高的帧率。6.2 TensorRT优化策略详解FP16精度如前所述在trtexec转换时加入--fp16参数。这是必选项能让推理速度提升近一倍而精度损失通常小于1% mAP。INT8量化这是更激进的优化。它需要一部分校准数据约500-1000张训练集图片来统计激活值的分布从而将权重和激活值从FP32/FP16量化到INT8。理论上能再提升1.5-2倍速度但精度损失可能更大2-5% mAP且转换过程更复杂。对于口罩检测这种二分类任务如果校准得当INT8是可行的。可以使用TensorRT的Python API编写校准程序或者使用trtexec的--int8和--calib参数。动态批处理Dynamic Batching我们的应用是单张图片推理所以动态批处理收益不大。但如果需要同时处理多路视频流可以在构建引擎时启用允许一次推理处理多张图片提高GPU利用率。层融合Layer FusionTensorRT在转换时会自动进行层融合例如将卷积、批归一化和激活函数融合为一个操作减少内核启动和内存访问开销。我们无需手动干预但要知道这是TensorRT性能提升的重要原因之一。6.3 系统与代码层优化技巧固定GPU频率使用sudo jetson_clocks命令可以让GPU和CPU锁定在最高频率避免因温控导致的降频。在持续推理场景下这能带来稳定的高性能。使用Jetson Stats监控安装jetson-stats(sudo pip3 install jetson-stats)运行jtop可以实时查看CPU/GPU频率、温度、内存和功耗。这是排查性能瓶颈的利器。视频解码优化如果输入是视频文件使用OpenCV的cv2.CAP_GSTREAMER后端可能比默认后端更高效。对于USB摄像头确保其驱动是V4L2并且设置合适的格式如MJPG和分辨率。内存与显存管理避免在推理循环中频繁创建和销毁大数组如预处理后的图像张量。在初始化时预分配好内存。使用pycuda的流cuda.Stream来管理异步的内存拷贝和内核执行充分利用GPU的并行能力。如果遇到CUDA out of memory错误首先检查转换引擎时的--workspace是否设置过高其次检查代码中是否有未释放的GPU内存。后处理优化后处理的解码和NMS步骤是在CPU上进行的如果检测框很多可能成为瓶颈。可以尝试使用向量化操作NumPy代替Python循环。将NMS的IoU阈值从0.45适当提高到0.5或0.6减少需要处理的框数量。如果速度仍然不够可以考虑使用CUDA编写自定义的TensorRT插件将后处理也放到GPU上但这复杂度较高。经过上述优化FP16 416x416输入系统优化我的Jetson Nano在处理640x480摄像头输入时推理帧率仅模型前向传播时间稳定在8-10 FPS包含前后处理和显示的整体帧率在6-8 FPS达到了基本实时的效果。7. 常见问题与故障排查实录在部署过程中你几乎一定会遇到下面这些问题。这里是我踩坑后的解决方案汇总。7.1 环境与依赖问题问题1ImportError: No module named ‘tensorrt’原因TensorRT的Python包路径未正确添加到Python环境中。解决# 找到tensorrt安装路径 find /usr -name “*tensorrt*.so” 2/dev/null # 通常Python包在 /usr/lib/python3.6/dist-packages/ # 将其添加到虚拟环境的site-packages或直接设置PYTHONPATH export PYTHONPATH/usr/lib/python3.6/dist-packages:$PYTHONPATH # 或者在代码开头添加 import sys sys.path.append(‘/usr/lib/python3.6/dist-packages’)问题2编译OpenCV时CMake报错找不到CUDA或编译失败原因依赖未装全或CUDA路径不对。解决确保安装了所有列出的依赖项。检查CUDA是否安装which nvcc。在CMake配置时可以指定CUDA_TOOLKIT_ROOT_DIR-D CUDA_TOOLKIT_ROOT_DIR/usr/local/cuda。如果编译过程中内存不足Nano内存小可以尝试make -j2减少并行编译进程数。7.2 模型转换与推理问题问题3trtexec转换ONNX模型时失败提示某些节点不支持原因ONNX模型中包含了TensorRT不支持的算子或者ONNX版本与TensorRT版本不兼容。解决确保用于导出ONNX的PyTorch或转换脚本与YOLOv4结构完全匹配。尝试使用不同版本的ONNX导出工具或转换脚本。一个常见问题是YOLO的“YoloLayer”或自定义操作。确保转换脚本正确地将这些层映射为标准的ONNX算子如Reshape, Concat, Sigmoid等。可以尝试使用TensorRT的Polygraphy工具来检查和修复ONNX模型polygraphy surgeon sanitize yolov4-mask.onnx --fold-constants --output yolov4-mask_cleaned.onnx。问题4推理时输出结果全是乱码或NaN原因 a) 预处理不一致训练时用的归一化是(x/255.0)而推理时可能用了(x-mean)/std。 b) 输入数据格式不对模型期望的是CHW格式的FP32数据但输入的是HWC或Uint8。 c) FP16精度下数值溢出较少见。解决严格对齐预处理检查训练代码和推理代码的预处理流程确保完全一致包括 resize 算法、归一化公式、通道顺序。打印中间值在预处理后打印输入张量的均值、最大值、最小值看是否在合理范围如0-1之间。检查引擎输入输出使用engine.get_binding_shape和engine.get_binding_dtype确认输入输出的维度和数据类型确保你的数据与之匹配。问题5帧率FPS远低于预期原因瓶颈可能不在模型推理。排查步骤分离计时分别记录预处理、推理、后处理、显示的时间。t1 time.time() # 预处理 t2 time.time() # 推理 t3 time.time() # 后处理 t4 time.time() # 显示 print(f“Preprocess: {t2-t1:.3f}s, Infer: {t3-t2:.3f}s, Postprocess: {t4-t3:.3f}s”)定位瓶颈如果预处理耗时高检查OpenCV的resize操作尝试使用cv2.INTER_LINEAR或cv2.INTER_NEAREST。如果后处理耗时高优化解码和NMS的代码使用NumPy向量化操作。如果显示耗时高cv2.imshow本身比较慢。可以考虑降低显示帧率或者将显示放到单独的线程。系统监控运行jtop观察GPU和CPU的利用率。如果GPU利用率未接近100%说明CPU预处理或后处理是瓶颈或者GPU没有被充分喂数据。7.3 部署与运行问题问题6运行一段时间后程序崩溃或系统卡死原因可能是内存/显存泄漏或散热问题导致过热降频/死机。解决检查内存使用jtop或tegrastats监控内存和显存使用情况。确保代码中没有在循环内不断分配大内存而不释放。加强散热Jetson Nano被动散热能力有限。务必加装散热风扇并确保风道畅通。可以运行sudo jetson_clocks --show查看当前温度和各核心频率是否因过热而下降。电源供应使用官方推荐的5V/4A电源适配器。供电不足会导致系统不稳定。问题7检测框抖动或漏检原因 a) 模型精度不足特别是对小目标或遮挡目标。 b) 视频流帧率低目标移动快导致模型跟不上。 c) NMS阈值或置信度阈值设置不合理。解决模型层面回顾训练数据增加更多小目标、遮挡、模糊的样本。可以尝试数据增强时加入更多的随机缩放和模糊。后处理层面适当降低置信度阈值如从0.5降到0.3召回更多目标但可能会增加误检。对连续视频帧可以加入简单的跟踪算法如IOU跟踪或卡尔曼滤波利用时序信息平滑检测框并减少漏检。业务层面对于出入口场景可以设置一个“检测区域”ROI只处理该区域内的人脸减少干扰和计算量。这个项目从构思到稳定运行前后调试了将近两周。最大的体会是边缘AI部署是一个系统工程任何一个环节——从数据标注、模型训练、格式转换、环境配置到代码优化——的疏忽都可能导致最终效果大打折扣。对于Jetson Nano这类资源紧张的设备“权衡”是贯穿始终的主题在分辨率、精度、速度、功耗之间找到最适合你具体场景的那个甜蜜点。最后多监控jtop、多测试不同光照、角度、多迭代模型和代码是保证项目成功上线的不二法门。希望这篇超详细的实践记录能帮你少走弯路。