简介这份资源是面向计算机、人工智能、自动化等专业学生与科研人员的毕设级部署方案聚焦将 mmdetection 与 TensorRT 集成到 ROS2 中支持 YOLOX 模型可在 Ubuntu 22.04 与 ROS2 Humble 环境下运行适合作为毕业设计、课程设计或项目立项演示也便于进阶学习者借鉴。压缩包共 190 个文件约 2.58MB涵盖 34 个 py 脚本、11 个 c 与 11 个 h 源文件、38 张 jpg 与 12 张 png 图示、22 个 js 及 json、txt、md 等配置与说明文档另有 C、CMake 相关工程文件结构完整、便于复现。资源包含源码、部署教程与详细设计文档代码经测试可稳定运行能帮助读者掌握模型转换、推理加速与 ROS2 节点通信的完整链路并理解工程目录组织与排错思路。目前已有 41 人学习适合需要快速搭建目标检测部署环境、对照文档完成调试的读者下载参考。1. TensorRT8 ROS2 部署 YOLOX一条从训练权重到机器人实时检测的落地链路很多做机器人感知的同行都遇到过这个尴尬YOLOX 在 PC 上用 PyTorch 跑得好好的一放到车载或嵌入式平台帧率直接掉到个位数CPU 占用拉满ROS2 节点还时不时卡死。这个标题讲的正是解决这类问题的完整方案——把 YOLOX 通过 TensorRT8 做推理加速再封装成 ROS2 节点让目标检测真正跑在机器人系统里。它适合两类人一是正在做毕设、需要一套能跑通、能写进论文的完整部署链路的学生二是已经在用 ROS2 做机器人开发、想把检测模型从“能跑”推到“实时跑”的工程师。核心链路是YOLOX 权重导出 ONNX用 TensorRT8 构建序列化引擎再写一个 ROS2 节点订阅图像话题、发布检测结果。下面按这个顺序拆开讲。2. 环境搭建TensorRT8、ROS2 与 YOLOX 的版本咬合关系2.1 为什么版本匹配比安装步骤更致命TensorRT8、CUDA、cuDNN、ROS2 发行版、PyTorch 这五者之间存在严格的版本约束不是随便装最新版就能跑通。常见做法是先确定 ROS2 发行版Humble 对应 Ubuntu 22.04再根据 Ubuntu 版本确定 CUDA 版本22.04 通常配 CUDA 11.8 或 12.x然后选与之匹配的 TensorRT8 小版本。YOLOX 官方仓库对 PyTorch 版本相对宽容但导出 ONNX 时的 opset 版本会影响 TensorRT 解析建议 opset 11。我一般会先跑一遍nvcc --version、python -c import tensorrt; print(tensorrt.__version__)、ros2 --version三条命令把当前环境摸清楚再动手。如果 TensorRT 是通过 pip 装的tensorrt包注意它和系统级 TensorRT 可能冲突建议二选一。提示TensorRT8 的 Python API 在不同小版本间有细微差异Builder、Network、Config的调用方式在 8.0 和 8.5 之间就有变化写代码前先确认自己的版本。2.2 从零装 ROS2 Humble 与 TensorRT8 的最小命令集假设系统是干净的 Ubuntu 22.04下面这套命令是我验证过的顺序。先装 ROS2 Humble# 设置 locale避免中文环境导致的编码问题 sudo apt update sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALLen_US.UTF-8 LANGen_US.UTF-8 # 添加 ROS2 源并安装 sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update sudo apt install curl -y sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null sudo apt update sudo apt install ros-humble-desktop python3-colcon-common-extensions -y装完后source /opt/ros/humble/setup.bash再验证ros2 run demo_nodes_cpp talker能否正常发消息。TensorRT8 的安装取决于你拿到的包形式如果是 tar 包解压后把lib加入LD_LIBRARY_PATH把python目录下的 whl 用 pip 装上如果是 deb 包直接dpkg -i即可。装完用python3 -c import tensorrt; print(tensorrt.__version__)确认。2.3 YOLOX 源码拉取与依赖安装的取舍YOLOX 官方仓库依赖torch、torchvision、onnx、onnxsim等。这里有个坑onnxsim在简化模型时可能改变输出节点名称导致后续 TensorRT 解析找不到输出。我的做法是导出 ONNX 后先不简化直接用trtexec或 Python API 构建引擎如果失败再考虑简化。git clone https://github.com/Megvii-BaseDetection/YOLOX.git cd YOLOX pip3 install -r requirements.txt pip3 install onnx onnxsim python3 setup.py developsetup.py develop会以可编辑模式安装 YOLOX方便后续改代码。依赖装完后下载官方预训练权重如yolox_s.pth放到weights目录跑一次python tools/demo.py image -n yolox-s -c weights/yolox_s.pth --path assets/dog.jpg确认推理正常。这一步能跑通说明 PyTorch 侧没问题可以进入导出环节。3. 从 PyTorch 权重到 TensorRT 引擎导出、构建与精度校验3.1 YOLOX 导出 ONNX 的关键参数与动态轴设置YOLOX 自带tools/export_onnx.py但默认导出的是静态 shape。如果你的 ROS2 节点输入图像尺寸固定比如 640×640静态 shape 反而更利于 TensorRT 优化。导出命令python3 tools/export_onnx.py \ --output-name yolox_s.onnx \ -n yolox-s \ -c weights/yolox_s.pth \ --opset 11 \ --input input \ --output output \ --dynamic--dynamic会启用动态 batch 和动态尺寸方便后续改分辨率。--input和--output指定输入输出节点名TensorRT 构建时要用到。导出后用onnxruntime跑一遍和 PyTorch 输出对比确认数值误差在 1e-3 以内。import onnxruntime as ort import numpy as np sess ort.InferenceSession(yolox_s.onnx) dummy np.random.randn(1, 3, 640, 640).astype(np.float32) out sess.run(None, {input: dummy}) print(out[0].shape) # 应为 (1, 8400, 85) 或类似如果输出 shape 不对检查导出时的--dynamic是否和模型结构匹配。YOLOX 的输出解码在后处理里做ONNX 只负责 backbone head 的原始输出。3.2 用 trtexec 快速验证 ONNX 能否被 TensorRT8 解析在写 Python 构建脚本之前先用trtexec做一次快速验证能省很多调试时间trtexec --onnxyolox_s.onnx \ --saveEngineyolox_s.engine \ --fp16 \ --workspace4096 \ --minShapesinput:1x3x640x640 \ --optShapesinput:1x3x640x640 \ --maxShapesinput:1x3x640x640--fp16开启半精度速度提升明显精度损失通常在可接受范围。--workspace是显存工作空间单位 MB4096 对 640 分辨率足够。如果trtexec报错常见原因是 opset 版本不兼容或某些算子不支持这时需要回退到 ONNX 简化或修改导出代码。3.3 Python API 构建引擎序列化、反序列化与推理封装实际部署时更推荐用 Python API 构建方便集成到 ROS2 节点里。下面是一个最小构建与推理封装import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit import numpy as np TRT_LOGGER trt.Logger(trt.Logger.WARNING) def build_engine(onnx_path, engine_path, fp16True): builder trt.Builder(TRT_LOGGER) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser trt.OnnxParser(network, TRT_LOGGER) with open(onnx_path, rb) as f: parser.parse(f.read()) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 30) if fp16: config.set_flag(trt.BuilderFlag.FP16) serialized builder.build_serialized_network(network, config) with open(engine_path, wb) as f: f.write(serialized) def load_engine(engine_path): with open(engine_path, rb) as f, trt.Runtime(TRT_LOGGER) as runtime: return runtime.deserialize_cuda_engine(f.read())EXPLICIT_BATCH是 TensorRT8 处理动态 batch 的必需标志。set_memory_pool_limit替代了旧版的max_workspace_size。构建完成后序列化到磁盘下次直接反序列化省去重复构建时间。推理时用execute_v2或execute_async_v3绑定输入输出显存指针。注意反序列化引擎时TensorRT 版本必须与构建时一致否则会失败。跨机器部署时要么带上构建环境要么在目标机上重新构建。4. ROS2 节点封装图像订阅、推理与检测结果发布4.1 节点结构设计与话题接口定义ROS2 节点的职责很清晰订阅sensor_msgs/Image做预处理送入 TensorRT 引擎后处理得到检测框发布自定义消息或vision_msgs/Detection2DArray。我一般会定义一个简单的自定义消息包含类别、置信度、bbox 四个坐标避免引入额外依赖。节点用rclpy写继承Node在__init__里创建订阅者和发布者在回调里做推理。注意 ROS2 的回调默认在单线程执行器里跑如果推理耗时较长会阻塞其他回调建议用MultiThreadedExecutor或把推理放到独立线程。import rclpy from rclpy.node import Node from sensor_msgs.msg import Image from vision_msgs.msg import Detection2DArray, Detection2D, BoundingBox2D from cv_bridge import CvBridge import cv2 import numpy as np class YoloxTRTNode(Node): def __init__(self): super().__init__(yolox_trt_node) self.sub self.create_subscription(Image, /camera/image_raw, self.callback, 10) self.pub self.create_publisher(Detection2DArray, /detections, 10) self.bridge CvBridge() self.engine load_engine(yolox_s.engine) self.context self.engine.create_execution_context() def callback(self, msg): frame self.bridge.imgmsg_to_cv2(msg, bgr8) blob self.preprocess(frame) outputs self.infer(blob) detections self.postprocess(outputs, frame.shape) self.publish(detections, msg.header)cv_bridge负责 ROS Image 和 OpenCV Mat 的转换。preprocess做 resize、归一化、通道转换。postprocess做 YOLOX 的解码和 NMS。发布时复用原消息的header保证时间戳一致。4.2 预处理与后处理的参数对齐预处理必须和训练时一致YOLOX 默认用 letterbox 填充到 640×640保持长宽比填充值 114。如果直接 resize 会改变长宽比导致检测框偏移。后处理的conf_threshold和nms_threshold根据场景调一般 0.25 和 0.45 起步。def preprocess(self, img, input_size640): h, w img.shape[:2] scale min(input_size / h, input_size / w) nh, nw int(h * scale), int(w * scale) resized cv2.resize(img, (nw, nh)) canvas np.full((input_size, input_size, 3), 114, dtypenp.uint8) canvas[:nh, :nw] resized blob canvas[:, :, ::-1].transpose(2, 0, 1).astype(np.float32) / 255.0 return np.ascontiguousarray(blob[None])letterbox 的 scale 和 padding 要记录下来后处理时把检测框映射回原图坐标。如果忘了这一步框会整体偏移这是最常见的翻车点之一。4.3 推理线程与 ROS2 执行器的配合TensorRT 推理是同步阻塞的如果放在 ROS2 回调里图像帧率高时会丢帧。我的做法是回调里只把图像塞进队列独立线程从队列取图推理推理完发布结果。队列用queue.Queue(maxsize1)只保留最新帧避免延迟累积。import threading, queue self.frame_queue queue.Queue(maxsize1) self.worker threading.Thread(targetself.infer_loop, daemonTrue) self.worker.start() def callback(self, msg): if self.frame_queue.full(): try: self.frame_queue.get_nowait() except queue.Empty: pass self.frame_queue.put(msg) def infer_loop(self): while rclpy.ok(): msg self.frame_queue.get() # 推理并发布这样即使推理慢于图像发布也不会阻塞订阅回调系统整体保持响应。daemonTrue保证主线程退出时工作线程自动结束。5. 避坑与排查部署 TensorRT8 ROS2 YOLOX 时最容易翻车的五件事5.1 引擎构建成功但推理结果全乱现象trtexec构建引擎没报错推理输出的框位置完全不对或置信度全是 0。原因通常是预处理和后处理没有对齐比如 letterbox 的 padding 值不对、归一化系数写错、或者输出节点的解码方式与 YOLOX 版本不匹配。解决先用同一张图分别跑 PyTorch 和 TensorRT逐层对比输出定位是预处理、网络输出还是后处理的问题。5.2 ROS2 节点启动后收不到图像话题现象节点正常运行但callback一直不触发。原因可能是话题名不匹配、QoS 配置不一致或者图像发布者的frame_id与订阅者期望不符。解决用ros2 topic list和ros2 topic info /camera/image_raw确认话题存在检查订阅时的 QoS 是否为SensorDataQoS图像话题通常用best_effort。5.3 TensorRT 反序列化时报版本不匹配现象在 A 机器构建的引擎拷贝到 B 机器反序列化失败报serialization version错误。原因是 TensorRT 版本或 CUDA 版本不一致。解决要么在目标机器上重新构建引擎要么确保两台机器的 TensorRT 小版本、CUDA 版本完全一致。跨平台部署时建议把 ONNX 和构建脚本一起带走。5.4 FP16 模式下检测精度明显下降现象开启 FP16 后小目标漏检增多置信度普遍偏低。原因是部分算子在半精度下数值范围不够。解决可以只对部分层开 FP16或者改用 INT8 校准。如果精度要求高直接跑 FP32速度损失用 batch 或分辨率来补。我一般会先用 FP16 跑一遍验证集mAP 掉超过 2 个点就回退。5.5 多线程下 CUDA 上下文冲突现象推理线程和 ROS2 回调线程同时访问 CUDA 资源程序随机崩溃或报invalid device context。原因是 PyCUDA 的上下文默认绑定到创建它的线程。解决在每个使用 CUDA 的线程里显式cuda.Context.attach()或detach()或者把所有 CUDA 操作集中到同一个线程里其他线程通过队列通信。6. 进阶技巧用 batch 推理和动态 shape 把吞吐再推一档当单帧推理跑通后下一步通常是提吞吐。TensorRT8 支持动态 shape可以在构建引擎时设置minShapes、optShapes、maxShapes让同一个引擎处理不同分辨率和 batch size。比如设置input:1x3x320x320到input:4x3x640x640运行时根据实际图像调整。这样在低负载时用小分辨率省算力高负载时用大 batch 提吞吐。另一个技巧是 batch 推理把多帧图像拼成一个 batch 送入引擎一次推理出多个结果。ROS2 节点里可以用一个固定大小的缓冲区攒够 N 帧或超时后统一推理。batch size 的选择要看显存和延迟要求T4 上 640 分辨率下 batch 4 通常还能保持实时。# 动态 shape 设置示例 profile builder.create_optimization_profile() profile.set_shape(input, min(1, 3, 320, 320), opt(1, 3, 640, 640), max(4, 3, 640, 640)) config.add_optimization_profile(profile)构建时加上这段引擎就能接受范围内的任意 shape。运行时通过context.set_input_shape(input, actual_shape)指定当前 shape再分配对应显存。注意每次 shape 变化后要重新分配输出显存否则会读到脏数据。验证方法上我习惯用trtexec --loadEngineyolox_s.engine --shapesinput:1x3x640x640 --iterations100 --avgRuns10跑基准看平均延迟和吞吐。然后在 ROS2 里用ros2 topic hz /detections看实际发布频率两者对比能发现节点封装带来的额外开销。如果 ROS2 侧频率明显低于 trtexec检查是不是 cv_bridge 转换或后处理拖了后腿。最后说个血泪经验每次改完预处理或后处理一定用同一张图跑一遍 PyTorch 和 TensorRT 的对比确认输出一致再上车。我因为偷懒跳过这一步在机器人上调试了一整天才发现是 letterbox 的 padding 值写成了 0 而不是 114。希望帮到你。本文还有配套的精品资源点击获取