Model Zoo YOLO FP32 模型 TROS 高效使用指南
最新方案:add ultralytics_yolo_output_parser to support YOLOv5-26 FP32 output models by Machao615 · Pull Request #42 · D-Robotics/hobot_dnn · GitHub
背景信息
社区开发者经常遇到一个棘手的问题:从开发者社区(Model Zoo)获取的 YOLO 系列模型(例如:RDK X5 YOLO参考这里,RDK S100 YOLO参考这里),无法直接在 TROS(TogetherROS)中使用。这主要是因为模型的输出格式与TROS中hobot_dnn节点的解析代码不兼容。
截至 2026年2月2日,我们已经充分意识到这一问题,并已启动一项旨在提升开发者体验的对Model Zoo 全面重构工作,其内容包括:
- 代码规范:统一代码风格与标准。
- 版本管理: 执行严格的版本管理标准。
- 目录结构:优化和统一项目结构。
- 文档规范:提供清晰、一致的中英文文档。
- 资源整合:逐步统一 TROS 和 Model Zoo 中的模型资源,包括
/app目录下的应用案例,最终实现无缝集成。
TROS预计在下一版本尽快完成统一工作。在此之前,您可以参考以下临时解决方案来适配当前最新的YOLO模型。
1. 问题描述
hobot_dnn 项目的后处理解析器(parser)当前期望其处理的YOLO模型输出是量化后(例如 int32_t 类型)的张量,因此代码中包含了反量化处理步骤。然而,当前 Model Zoo 中最新的 YOLO 模型已更新,其输出层直接输出未经量化的 float (FP32) 浮点型张量。
这种变化导致了数据格式的不兼容:原有的解析器会错误地将 FP32 数据当作 int32_t 处理,导致解析失败和错误的检测结果。
2. 解决方案概述
核心解决方案是修改与您使用的YOLO模型相对应的 C++ 输出解析器,使其能够正确处理 FP32 数据。这主要涉及两个关键点:
- 更改用于读取模型输出张量的数据类型。
- 移除不再需要的反量化(Dequantization)处理步骤。
本指南以 YOLOv8 模型为例,对应的文件是 ptq_yolo8_output_parser.cpp。同样的方法也适用于其他遇到类似问题的 YOLO 模型解析器,目前Model Zoo中的YOLO方案后处理通用。
3. 分步修改指南
以下步骤将指导您如何修改 src/hobot_dnn/dnn_node/src/output_parser/detection/ptq_yolo8_output_parser.cpp 文件。
步骤 3.1: 更改包围框(Bounding Box)的数据类型
在 ParseTensor 函数中,找到获取包围框张量数据的代码行。您需要将指针类型从 int32_t* 修改为 float*。
找到此行:
int32_t *box_data = boxes->GetTensorData<int32_t>();
并将其修改为:
float *box_data = boxes->GetTensorData<float>();
步骤 3.2: 移除反量化相关的代码
由于模型直接输出浮点数,反量化函数 DequantiScale 和相关的 box_scale_data 变量都不再需要。
找到并删除这一行:
auto *box_scale_data = reinterpret_cast<float *>(boxes->properties.scale.scaleData);
步骤 3.3: 修改核心解析逻辑
这是最关键的一步。我们需要修改解析包围框分布的代码,直接使用从 cur_box_data 中读取的浮点数,而不是先进行反量化。
找到此代码块:
int32_t *cur_box_data = box_data;
// ... (中间省略几行) ...
for (size_t i = 0; i < 4; ++i) {
sum = 0.;
for (int reg = 0; reg < reg_max; ++reg) {
if (is_performance_) {
distribute_score = fastExp(DequantiScale(cur_box_data[box_id], false, box_scale_data[box_id]));
} else {
distribute_score = std::exp(DequantiScale(cur_box_data[box_id], false, box_scale_data[box_id]));
}
sum += distribute_score;
decoded_boxes[i] += distribute_score * reg;
++box_id;
}
decoded_boxes[i] /= sum;
}
将其替换为以下简化版本:
float *cur_box_data = box_data;
// ... (中间省略几行) ...
for (size_t i = 0; i < 4; ++i) {
sum = 0.;
for (int reg = 0; reg < reg_max; ++reg) {
if (is_performance_) {
distribute_score = fastExp(cur_box_data[box_id]);
} else {
distribute_score = std::exp(cur_box_data[box_id]);
}
sum += distribute_score;
decoded_boxes[i] += distribute_score * reg;
++box_id;
}
decoded_boxes[i] /= sum;
}
步骤 3.4: 移除不再使用的 DequantiScale 函数
为了保持代码整洁,请移除已经不再使用的 DequantiScale 函数的声明和定义。
从文件中删除以下两个部分:
-
函数前向声明 (通常在文件靠前的位置):
float DequantiScale(int32_t data, bool big_endian, float &scale_value); -
函数定义 (通常在文件的末尾):
float DequantiScale(int32_t data, bool big_endian, float &scale_value) { return static_cast<float>(r_int32(data, big_endian)) * scale_value; }
4. 编译与部署指南
完成代码修改后,您需要重新编译功能包,并让TROS环境加载您本地修改后的版本。以下是详细步骤。
步骤 4.1: 编译功能包并指定目标平台
hobot_dnn 的编译需要明确指定目标硬件平台。您需要通过CMake参数来完成。
- 回到您的ROS2工作空间根目录,例如
/root/tros_ws。 - 执行
colcon build命令,并使用--cmake-args来传递平台参数。
编译命令示例:
-
为 S100 平台编译:
colcon build --packages-select dnn_node --cmake-args -DPLATFORM_S100=ON -
为 X5 平台编译:
colcon build --packages-select dnn_node --cmake-args -DPLATFORM_X5=ON
编译成功后,dnn_node 的新版本库文件将生成在您工作空间的 install/dnn_node/ 目录下。
步骤 4.2: 部署与覆盖TROS环境
为了让系统使用您刚刚编译的版本,而不是TROS系统自带的版本,您需要利用ROS2的 工作空间覆盖(Overlay) 机制。原理是,ROS2会优先使用您最后 source 的工作空间中的功能包。
-
打开一个新的终端。这是一个干净的环境,确保不会被旧的环境变量干扰。
-
首先,
sourceTROS 的主环境。这会加载TROS系统的所有基础功能包。source /opt/tros/humble/setup.bash -
然后,
source您本地的工作空间。这个工作空间包含了您刚刚修改并编译的dnn_node。# 注意:请确保路径是您自己的工作空间路径 source /root/tros_ws/install/setup.bash这个顺序至关重要!后
source的会覆盖先source的。 -
(可选)验证覆盖是否成功。您可以运行以下命令来检查当前环境中
dnn_node功能包的路径:ros2 pkg prefix dnn_node如果覆盖成功,输出的路径应该指向您本地的工作空间,例如:
/root/tros_ws/install/dnn_node如果输出的是
/opt/tros/humble/share/dnn_node或类似路径,则说明覆盖未成功,请检查您的source顺序和路径是否正确。
完成以上步骤后,您在该终端中启动的任何ROS2节点(例如通过 ros2 launch)都将自动使用您本地修改过的 dnn_node 版本,从而能够正确处理FP32模型输出。