【保姆级教程】地平线 RDK X5 部署 YOLO 模型 (Python+ROS2) 并实现画面话题发布
2026/4/6 4:35:13 网站建设 项目流程
前言地平线 RDK X5 是一款拥有 5 TOPS 算力的强大边缘计算板。很多开发者在上面部署 YOLO 目标检测模型时往往觉得 Python 跑视觉会很卡急于使用 C 重写。 但实际上地平线官方提供的 Python 库pyeasy_dnn底层是基于 C 零拷贝实现的只要我们的后处理代码如解算框、NMS优化得当纯 Python 节点也能轻松跑满相机的物理帧率极限15~30 FPS而且 CPU 占用极低本文将手把手教你如何在一个纯净的 ROS2 环境中使用 Python 快速部署编译好的 YOLO.bin模型订阅相机画面进行推理并将画好检测框的画面通过 ROS2 话题重新发布出来供电脑端rqt实时查看 准备工作上传 YOLO 模型文件 (.bin)在开始编写代码之前请确保你已经通过地平线的工具链PTQ/QAT将训练好的 YOLO 模型转换为了 BPU 能够识别的.bin格式。通过 MobaXterm、WinSCP 或 SCP 命令将你的yolov5.bin文件上传到 RDK X5 开发板上。建议存放位置你可以将其放在/root/yolov5.bin或者你的工作空间目录下例如~/yolo_ws/yolov5.bin。注意在后续的 Python 代码中有一个变量叫self.model_path请务必将其修改为你实际存放.bin文件的绝对路径️ 第一步创建 ROS2 工作空间与功能包首先我们需要在 RDK X5 的终端中建立一个专门用于视觉推理的 ROS2 Python 功能包。打开终端依次输入以下命令# 1. 创建工作空间目录 mkdir -p ~/yolo_ws/src cd ~/yolo_ws/src # 2. 创建基于 Python 的 ROS2 功能包 ros2 pkg create --build-type ament_python yolo_vision此时你的工作空间中就多出了一个名为yolo_vision的功能包。 第二步编写极速版推理节点代码接下来我们编写核心的推理代码。这份代码内置了高度优化的 NumPy 矩阵加速解码逻辑告别传统的 Pythonfor循环遍历数万个预测框导致的卡顿问题。1. 进入代码目录并创建文件cd ~/yolo_ws/src/yolo_vision/yolo_vision nano yolo_inference.py2. 复制并粘贴以下完整代码(注意请将代码中self.model_path和self.subscription的话题名称修改为你自己的实际路径和相机话题)#!/usr/bin/env python3 import rclpy from rclpy.node import Node from sensor_msgs.msg import Image from cv_bridge import CvBridge import cv2 import numpy as np from rclpy.qos import qos_profile_sensor_data from hobot_dnn import pyeasy_dnn as dnn import time from rclpy.executors import ExternalShutdownException class YoloInferenceNode(Node): def __init__(self): super().__init__(yolo_inference_node) self.bridge CvBridge() # 1. 加载地平线 BPU 模型 (.bin 文件) self.model_path /root/yolov5.bin # ⚠️ 请修改为你的 bin 模型实际路径 self.get_logger().info(f正在加载 BPU 模型: {self.model_path}) self.models dnn.load(self.model_path) self.model self.models[0] self.get_logger().info(BPU 模型加载成功) # 2. 订阅原始相机图像话题 # ⚠️ 请修改为你实际的相机话题例如 /camera/image_raw self.subscription self.create_subscription( Image, /image_raw, self.image_callback, qos_profile_sensor_data) # 3. 创建发布者将画好框的图像发出去 self.img_pub self.create_publisher(Image, /yolo_result_image, 10) self.last_time time.time() # 将 BGR 图像转换为 BPU 硬件需要的 NV12 格式 def bgr2nv12(self, bgr_image): height, width bgr_image.shape[:2] yuv420p cv2.cvtColor(bgr_image, cv2.COLOR_BGR2YUV_I420) y yuv420p[:height, :] u yuv420p[height: height height // 4, :].reshape(height // 2, width // 2) v yuv420p[height height // 4:, :].reshape(height // 2, width // 2) nv12 np.empty((height * 3 // 2, width), dtypenp.uint8) nv12[:height, :] y nv12[height:, 0::2] u nv12[height:, 1::2] v return nv12 # 核心优化NumPy 矩阵加速解码极大地降低 CPU 占用 def decode_yolo_outputs(self, outputs, conf_thres0.4, iou_thres0.45): pred np.squeeze(outputs[0].buffer) if pred.shape[0] pred.shape[1]: pred pred.transpose() # 批量过滤低置信度的框 obj_conf pred[:, 4] valid_indices obj_conf conf_thres valid_pred pred[valid_indices] if len(valid_pred) 0: return [] cls_probs valid_pred[:, 5:] class_ids np.argmax(cls_probs, axis1) scores valid_pred[:, 4] * cls_probs[np.arange(len(valid_pred)), class_ids] score_indices scores conf_thres valid_pred valid_pred[score_indices] scores scores[score_indices] class_ids class_ids[score_indices] if len(valid_pred) 0: return [] cx, cy, w, h valid_pred[:, 0], valid_pred[:, 1], valid_pred[:, 2], valid_pred[:, 3] x_min, y_min cx - w / 2, cy - h / 2 boxes np.column_stack((x_min, y_min, w, h)).astype(int).tolist() scores scores.astype(float).tolist() class_ids class_ids.astype(int).tolist() # NMS 非极大值抑制 indices cv2.dnn.NMSBoxes(boxes, scores, conf_thres, iou_thres) results [] if len(indices) 0: for i in indices.flatten(): box boxes[i] results.append([box[0], box[1], box[0] box[2], box[1] box[3], scores[i], class_ids[i]]) return results def image_callback(self, msg): try: # 计算 FPS current_time time.time() fps 1.0 / (current_time - self.last_time) if (current_time - self.last_time) 0 else 0 self.last_time current_time # 1. 接收 ROS 图像并转为 OpenCV 格式 cv_image self.bridge.imgmsg_to_cv2(msg, bgr8) # 2. 图像预处理 (缩放至模型所需大小并转为 NV12) resized_image cv2.resize(cv_image, (640, 640)) nv12_image np.ascontiguousarray(self.bgr2nv12(resized_image)) # 3. 提交 BPU 推理 outputs self.model.forward(nv12_image) # 4. 解析输出 bboxes self.decode_yolo_outputs(outputs, conf_thres0.4) # 5. 在图像上绘制识别框 for box in bboxes: x_min, y_min, x_max, y_max, score, class_id box # 画框 cv2.rectangle(resized_image, (int(x_min), int(y_min)), (int(x_max), int(y_max)), (0, 255, 0), 2) # 写上类别 ID 和置信度 label_text fClass:{class_id} {score:.2f} cv2.putText(resized_image, label_text, (int(x_min), int(y_min) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) # 打印 FPS (左上角) cv2.putText(resized_image, fFPS: {fps:.1f}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2) # 6. 将画好框的图像转回 ROS 消息并发布 result_msg self.bridge.cv2_to_imgmsg(resized_image, bgr8) result_msg.header msg.header self.img_pub.publish(result_msg) except Exception as e: self.get_logger().error(f处理图像时发生异常: {e}) def main(argsNone): rclpy.init(argsargs) node YoloInferenceNode() try: rclpy.spin(node) except (KeyboardInterrupt, ExternalShutdownException): node.get_logger().info(检测到 CtrlC节点正在退出...) finally: if rclpy.ok(): node.destroy_node() rclpy.try_shutdown() if __name__ __main__: main()保存并退出编辑器。⚙️ 第三步配置并编译环境我们需要让 ROS2 知道这个节点的执行入口在哪里。1. 编辑setup.py文件cd ~/yolo_ws/src/yolo_vision nano setup.py2. 找到entry_points部分将其修改为如下内容entry_points{ console_scripts: [ # 注册我们的执行节点 yolo_node yolo_vision.yolo_inference:main, ], },保存并退出。3. 编译工作空间cd ~/yolo_ws colcon build 第四步运行与远程可视化测试激动人心的时刻到了我们将启动这个节点并在电脑端实时查看地平线 BPU 的推理结果。1. 启动你的相机节点请在一个终端中优先启动你自己的相机节点确保它正在向/image_raw或你指定的 topic发布图像。2. 启动 YOLO 推理节点打开一个新终端加载刚才编译好的环境并运行cd ~/yolo_ws source install/setup.bash ros2 run yolo_vision yolo_node如果看到终端打印出[INFO] [...] [yolo_inference_node]: BPU 模型加载成功说明模型已经开始飞速运转了3. 开启上帝视角RQT 可视化确保你的电脑装有 ROS2 的 Ubuntu和 RDK X5 在同一个局域网Wi-Fi下。 在电脑端的终端中输入ros2 run rqt_image_view rqt_image_view在弹出的界面左上角下拉菜单中选择我们刚才发布的预测话题/yolo_result_image。大功告成你现在应该能清晰地看到带检测框、置信度并且带有实时 FPS 统计的 YOLO 推理画面了借助我们内置的 NumPy 加速帧率表现应该极其出色。小贴士如果遇到画面倒置可以在代码中读取cv_image之后加上一句cv_image cv2.flip(cv_image, -1)即可轻松解决。希望这篇保姆级教程能帮你在地平线 RDK X5 上顺利点亮计算机视觉的第一把火如有疑问欢迎在评论区交流探讨。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询