RDK YOLO Develop (v1.0)
YOLO 训练 → 地瓜 RDK X5 BPU 量化部署 → TROS / ROS2 集成的端到端 Agent 协作 Skill。
支持 YOLOv5u / v8 / v9 / v11 / v13 / YOLO26 全系列的检测 / 分割 / 姿态 / OBB / 分类任务。
端到端流程概览
[训练机 x86+GPU] [量化机 x86 Linux] [板端 RDK X5]
│ │ │
阶段 0 阶段 1 阶段 2 阶段 3 阶段 -1 / 4 阶段 5
conda env ──▶ 数据集 ──▶ yolo train ──▶ ONNX 导出 ──▶ hb_mapper ──▶ 板端自检 ──▶ Python 推理 ──▶ TROS / ROS2
(一次性) train/val/cal best.pt best.onnx best_*.bin_runtime dnn_node
mAP > 0.7 NHWC 6 输出 cosine ≥ 0.99 Forward < 30ms 9~30 Hz
/hobot_dnn_detection
最常见路径:训练机 = 量化机(同一台 x86 + GPU),ONNX 不出训练机,只有 *.bin 跨机 scp 到 RDK X5。
触发条件
用户提到:地瓜机器人 / RDK X5 / BPU / bayes-e / hb_mapper / HobotDnn / TROS / ultralytics_yolo 量化部署
🤖 Agent 启动协议(按需追问,不一次性问完)
涉及三个环境:训练环境(x86+GPU)/ 量化环境(x86 Linux)/ 板端环境(RDK X5 aarch64)。
不要一次性问"1A 2B 3A"这种死板格式。用自然语言对话,到哪个阶段问哪个环境,没回答之前绝不假设。
启动开场白(第一句话,简短)
我会带你完成 YOLO → RDK X5 BPU 一条龙部署:训练 → ONNX 导出 → 量化 → 板端推理(→ 可选 ROS2)。
为了直接帮你跑命令而不是只给指导,需要先了解几台机器你怎么接入。
我会按阶段分别问你,不一次性堆所有问题。
先从最近要做的开始:训练机你打算怎么用?
各阶段询问点
进入训练阶段前(阶段 0 / 阶段 2)— 问训练机:
训练机你打算怎么用?常见三种方式我都支持:
• 我现在的工作目录就在 GPU 机器上 → 直接帮你跑
• GPU 在另一台服务器上 → 给我 SSH(host/user/port,假设 ssh key 已 copy),我远程操作
• 你只想要命令清单,自己手动跑 → 我输出指导,你回报 mAP
也可以直接告诉我:"本机就行" / "ssh 到 192.168.x.x" / "给我命令"。
进入量化阶段前(阶段 3)— 只在用户没顺手提到时问:
量化机就跟训练机一台对吧?(hb_mapper 不需要 GPU,只要 x86 Linux)
如果是另一台,给我 SSH 信息;如果都没有量化机,我可以输出指导。
90% 的情况量化机 = 训练机(同一台 x86 Linux),不要无脑追问。
进入板端阶段前(阶段 -1 / 阶段 4 / 阶段 5)— 问 RDK X5:
RDK X5 板端怎么接?
• 给我 SSH(root@<IP>,ssh key 已 copy)→ 我直接连上去做环境检查、传文件、跑推理、启动 ROS2
• 你来手动跑 → 我把脚本和命令给你
如果用户在最初一次性给了所有 SSH 信息("训练机 ssh xxx,板端 ssh yyy"),就不用再分别问,直接进入。核心原则:未表达的环境保留未知,需要时再问;表达过的不重复问。
执行档位(统一 4 档,不再用 ABCDE)
| 档位 | 触发条件 | Agent 行为 |
|---|---|---|
| 本机直跑 | 用户在 GPU 机器/x86 Linux 上调用 Agent | 直接用 Bash 工具跑 yolo train / mapper.py |
| SSH 远程 | 用户给了 host/user/port | 用 ssh -p PORT user@host 'bash -s' < script.sh 远程跑,tmux 或 nohup 防断开 |
| 输出指导 | 用户明说"只要命令" | 输出完整命令清单 + 校验点,要求用户回报关键指标(mAP、bin 大小、forward 时间) |
| 板端 SSH | 用户给了 RDK X5 SSH | ssh + scp 全自动;板端默认无 tmux,用 nohup ... & 兜底 |
SSH 安全约定
- 不索要明文密码,假设用户配好 SSH key
- 连不上时提示
ssh-copy-id user@host(非默认端口加-p PORT) - 长时间任务(训练、ROS2 节点)必须用
tmux或nohup防 SSH 断开 - 跨 SSH 传脚本永远走
ssh ... 'bash -s' < local_script.sh,不要把长命令塞进ssh ... "..."— 引号嵌套会被随机吞字符(实测:pip install被切成pip iuiet,dataset/data.yaml被切成datasetta.yaml,找半天 bug) - scp 多源限制:新 OpenSSH(≥ 9.0)
scp src1 src2 host:dst行为变了,建议逐个 scp 或scp -r整目录
跨机文件流转
绝大多数实际场景训练机 = 量化机(都是 x86 Linux + GPU),ONNX 不出训练机:
[最常见] 训练机(GPU x86) ─── *.bin ───▶ RDK X5
[较少见] 训练机 → 量化机 → RDK X5(两段 scp)
[纯本地] 本机一条龙
⚠️ 板端环境硬性要求(最先验证)
板端 SSH 档位 Agent 直接 ssh root@<RDK_IP> 'bash -s' < check_env.sh;输出指导档位 Agent 把脚本贴给用户让他自己跑:
#!/bin/bash
echo "=== RDK X5 部署环境自检 ==="
# 1. RDK OS ≥ 3.5.0(hbm_runtime 前提)
OS_VERSION=$(cat /etc/version)
echo "OS: $OS_VERSION"
[ "$(printf '%s\n' "3.5.0" "$OS_VERSION" | sort -V | head -n1)" = "3.5.0" ] && echo " ✅" || echo " ❌ 需 ≥ 3.5.0"
# 2. TROS Humble setup
[ -f /opt/tros/humble/setup.bash ] && echo "TROS Humble: ✅" || echo "TROS Humble: ❌"
# 3. dnn_node ≥ 2.6.1(ultralytics_yolo parser 引入)
DNN_VER=$(cat /opt/tros/humble/share/dnn_node/package.xml | grep -oP '(?<=<version>)[^<]+')
echo "dnn_node: $DNN_VER"
[ "$(printf '%s\n' "2.6.1" "$DNN_VER" | sort -V | head -n1)" = "2.6.1" ] && echo " ✅" || echo " ❌ apt upgrade tros-humble-dnn-node"
# 4. hbm_runtime
python3 -c "import hbm_runtime" 2>/dev/null && echo "hbm_runtime: ✅" || echo "hbm_runtime: ❌ pip install hbm-runtime"
# 5. BPU 设备节点
ls /dev/bpu* 2>/dev/null && echo "BPU device: ✅" || echo "BPU device: ❌"
# 6. python opencv numpy(runtime 用得到)
python3 -c "import cv2, numpy; print('cv2', cv2.__version__, '| numpy', numpy.__version__)" 2>&1
# 7. tmux(板端默认 OS 镜像不带,没有就用 nohup)
which tmux 2>/dev/null && echo "tmux: ✅" || echo "tmux: ❌(不重要,Agent 会用 nohup)"
dnn_node 版本节点:
tros_2.6.1(2026-02-25):ultralytics_yoloparsertros_2.6.2(2026-05-08):yolo26_segparser
实测基线(RDK X5 / OS 3.5.0-beta / dnn_node 2.6.1):
- BPU Platform Version 1.3.6, HBRT 3.15.55.0, hb_mapper 1.24.3
/dev/bpu,/dev/bpu_core0双节点存在- cv2 4.11.0, numpy 1.26.4 已自带
自定义支持
类别数:✅ 完全自由
唯一约束:HobotDnn 的 class_num 必须等于 cls_names_list 文件行数。
分辨率:⚠️ 仅正方形且能被 32 整除
合法值:320/384/416/480/512/576/640/768/896/1024/1280
默认:检测/分割/姿态/OBB = 640,分类 = 224
export_monkey_patch.py 不传 imgsz,继承训练 imgsz。改尺寸:训练命令加 imgsz=N,或改脚本 m.export(..., imgsz=N)
两个 Sample 能力
| Sample | 检测 | 分割 | 姿态 | OBB | 分类 | |--------|-----|-----|-----|-----|-----| | ultralytics_yolo | YOLOv5u~v13 ✅ | YOLOv8/v9/11 ✅ | YOLOv8/11 ✅ | ⚠️ TODO | YOLOv8/11 ✅ (224) | | ultralytics_yolo26 | n/s/m/l/x ✅ | ✅ | ✅ | ✅ 唯一支持 | ✅ (224) |
七大易错点(真机踩坑总结)
- dnn_Parser:用户量化的模型(box 含反量化节点)一律用
"ultralytics_yolo",不要用"yolov8"⚠️ 官方/opt/tros/humble/lib/dnn_node_example/config/yolov11workconfig.json默认是yolov8,会完全错检,复制后必须改 - utils/ 必传:
main.py用相对路径导入utils/py_utils/,且需要保持samples/vision/<model>/runtime/python/这种 5 层深度(脚本里有sys.path.append("../../../../../")) - export_monkey_patch.py 用
simplify=False:不要改成 True,会破坏 BPU 优化路径 - rdkx5-yolo-mapper 隐性依赖 setuptools:新版 conda env 不带
pkg_resources,量化会在horizon_nn.build_onnx阶段抛ModuleNotFoundError: No module named 'pkg_resources'。装包后必须pip install setuptools - conda 26+ 必须先接受 ToS:
conda create直接报CondaToSNonInteractiveError,需先conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main && conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r - pip 国内网络:torch / ultralytics 直连 pypi 常超时挂死。国内必须换清华源
-i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 60 --retries 10 - Arial.ttf 下载会卡死训练:
ultralytics首次会去https://ultralytics.com/assets/Arial.ttf拉字体,SSL 失败会无限重试堵住整个 train。提前放占位:cp /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf ~/.config/Ultralytics/Arial.ttf
dnn_Parser 速查
| 系列 | dnn_Parser | reg_max | model_output_count |
|------|-----------|---------|-------------------|
| YOLOv5u~v13 检测 | "ultralytics_yolo" | 16 | 6 |
| YOLO26 检测 | "ultralytics_yolo" | 1 | 6 |
| YOLOv8/v9/11 分割 | "yolov8_seg" | 16 | 10 |
| YOLO26 分割 | "yolo26_seg" | — | 10 |
六阶段流水线(Agent 在每阶段根据用户选择走分支)
阶段 0:环境
本机直跑 → 在 GPU 机器上直接装;SSH 远程 → 用 ssh ... 'bash -s' < install.sh 到训练机上装;输出指导 → 把下面命令贴给用户
# Step 1: conda 环境 + ToS(conda 26+ 必须)
conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main
conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r
conda create -n rdk_env python=3.10 -y && conda activate rdk_env
# Step 2: 国内必须换源装 PyTorch(pypi 直连必超时)
PIP_OPTS="-i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 60 --retries 10"
pip install $PIP_OPTS torch torchvision --index-url https://download.pytorch.org/whl/cu121
pip install $PIP_OPTS "ultralytics>=8.3.0" rdkx5-yolo-mapper onnx onnxsim
# Step 3: 修 rdkx5-yolo-mapper 缺的 setuptools(不装会在量化阶段挂掉!)
pip install $PIP_OPTS setuptools
# Step 4: 字体占位防 ultralytics 卡 SSL 下载
mkdir -p ~/.config/Ultralytics
cp /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf ~/.config/Ultralytics/Arial.ttf
# Step 5: 验证(任何一项失败请回到对应 Step 修复,不要往下走)
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
python -c "import pkg_resources; print('setuptools OK')" # 这步漏了 → 阶段 3 量化必挂
hb_mapper --version # 应输出 1.24.x
ls ~/.config/Ultralytics/Arial.ttf # 字体占位文件应存在
WSL2 GPU 排查:如果 nvidia-smi 不在 PATH,检查 /usr/lib/wsl/lib/nvidia-smi,把这路径加进 ~/.bashrc 即可(不需要重装驱动)。
阶段 1:数据
1.1 目录结构
project/
├── dataset/
│ ├── images/
│ │ ├── train/ ← 训练集
│ │ └── val/ ← 验证集(20 ~ 50 张即可)
│ ├── labels/
│ │ ├── train/
│ │ └── val/
│ └── data.yaml ← path/train/val/names
└── calibration_data/ ← 校准集(量化用,20 ~ 50 张即可,可直接复用 val 或 train 子集)
1.2 三种集合的作用 & 推荐张数
| 集合 | 用途 | 推荐张数 | Skill 强约束 |
|---|---|---|---|
| 训练集 (train) | yolo train 学权重 | 越多越好(典型 100+) | 无 |
| 验证集 (val) | 训练中算 mAP,YOLO 自动跑 | 20 ~ 50 张 | 至少 1 类有标注框,否则 mAP 算不出来 |
| 校准集 (calibration) | hb_mapper 量化时统计激活分布 | 20 ~ 50 张(mapper.py 硬编码警告:< 20 可能失败,> 50 慢) | 必须是与生产场景同分布的图(直接抽训练集或 val 即可) |
实战准则:
- 验证集和校准集张数可以相同(都 20-50),但不要直接共用同一个目录 — val 是 YOLO 推理用 jpg+txt 对,校准集只要 jpg 不要 txt
- 校准集最简单的做法:
cp dataset/images/train/*.jpg calibration_data/(随机抽 30 张也行) - 实测:训练集 32 张 + 验证集 8 张 + 校准集 32 张全程跑通(量化 cosine sim 全 ≥ 0.99,板端检测正确),数据量少照样能 demo,但 mAP 容易过拟合,生产场景建议每类 ≥ 50 张训练样本
1.3 data.yaml 示例
path: /home/user/project/dataset # ⚠️ 远端跑训练时必须绝对路径,相对路径会找不到图
train: images/train
val: images/val
names:
0: park
1: qrcode
2: obstacle
1.4 数据完整性检查(阶段 1 Checkpoint)
# 训练集和验证集 image 数 = label 数
echo "train_imgs:$(ls dataset/images/train | wc -l) train_lbls:$(ls dataset/labels/train | wc -l)"
echo "val_imgs:$(ls dataset/images/val | wc -l) val_lbls:$(ls dataset/labels/val | wc -l)"
# 校准集只数 jpg
echo "cal_imgs:$(ls calibration_data/ | wc -l)"
任何一对 imgs/lbls 不匹配 → 训练会报 WARNING: <N> missing labels,必须修;val 全空白会 mAP=0。
阶段 2:训练
yolo detect train model=yolo11n.pt data=dataset.yaml epochs=100 imgsz=640 batch=16 device=0
# 自定义分辨率:imgsz=1280(必须 32 倍数)
SSH 远程训练(推荐做法):
- 本地 Write 一个
run_train.sh(含 conda activate + yolo 命令) - 远端 tmux 启动后台跑:
ssh user@gpu 'bash -s' << 'OUTER'
cat > /tmp/run_train.sh << 'INNER'
#!/bin/bash
source ~/miniconda3/etc/profile.d/conda.sh
conda activate rdk_env
cd /home/marcelo/yolo_ws
yolo detect train model=yolo11s.pt data=dataset/data.yaml epochs=100 imgsz=640 batch=16 device=0 project=runs name=yolov11s_park exist_ok=True 2>&1 | tee train.log
INNER
chmod +x /tmp/run_train.sh
tmux new -d -s yolo_train "/tmp/run_train.sh"
OUTER
# 后续监控:ssh user@gpu "tail -50 /home/marcelo/yolo_ws/train.log"
⚠️ 不要把 yolo 整条长命令塞进 tmux new -d -s X "yolo train ..." 字符串里 — 引号嵌套加 ssh 转义会出 typo(实测:dataset/data.yaml 被吞成 datasetta.yaml)。
阶段 3:ONNX + 量化
# ONNX 导出(注意参数:ultralytics_yolo 用 --pt,YOLO26 用 --weights)
python3 export_monkey_patch.py --pt best.pt # ultralytics_yolo
python3 export_yolo26_detect_bpu.py --weights best.pt # YOLO26
# 量化(cal-sample-num 实际能用的不超过 cal-images 实际张数)
python3 mapper.py --onnx best.onnx --cal-images ./calibration_data \
--cal-sample-num 50 --optimize-level O3 --output-dir .
ONNX 导出验证(Detect 头被替换成 BPU 友好版的标志):
[Cauchy] Replaced Detect_forward in 23
[Cauchy] Replaced Attention_forward in attn # YOLOv11 的 C2PSA 模块
output shape: ((1,80,80,3), (1,80,80,64), (1,40,40,3), ...) # NHWC + 6 输出
量化耗时基准(实测 RTX 3060 Laptop / Ryzen 7 5800H WSL2):
- ONNX 导出:~1-2 秒
- hb_mapper makertbin O3 + 32 张校准:~24 分钟(无 GPU 加速,纯 CPU)
- 输出:
<onnx_basename>_bayese_640x640_nv12.bin,YOLOv11s = 9.9MB
量化精度合格线:日志末尾 Output cosine similarity 全部 ≥ 0.99,单层最低 ≥ 0.98 即认为通过。
阶段 4:板端 Python(板端 SSH 档位下 Agent 自动跑)
注:下面所有
/home/root/路径都假设以 root 登录板端(RDK X5 出厂默认 root@<IP>)。如果用普通用户sunrise@<IP>,路径替换为/home/sunrise/。
目录结构要保持 rdk_model_zoo 的 5 层深度(main.py 里 sys.path.append("../../../../../")),否则 import utils.py_utils 会失败:
/home/root/inference/rdk_model_zoo/
├── samples/vision/ultralytics_yolo/
│ ├── runtime/python/ # main.py + ultralytics_yolo_det.py etc
│ ├── model/ # *.bin 放这里
│ └── test_data/ # 测试图 + custom_classes.names
└── utils/py_utils/ # ⚠️ 必传,main.py 相对路径导入
# 1) 建目录(保留 rdk_model_zoo 命名)
ssh root@<IP> 'mkdir -p /home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/{runtime/python,model,test_data} /home/root/inference/rdk_model_zoo/utils'
# 2) 传文件(注意 scp 多源不可靠,逐个传或用 -r 整目录)
scp -r rdk_model_zoo/samples/vision/ultralytics_yolo/runtime/python/. root@<IP>:/home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/runtime/python/
scp -r rdk_model_zoo/utils/. root@<IP>:/home/root/inference/rdk_model_zoo/utils/
scp best_bayese_640x640_nv12.bin root@<IP>:/home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/model/
scp test.jpg root@<IP>:/home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/test_data/
scp custom_classes.names root@<IP>:/home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/test_data/
# 3) 推理(cwd 必须是 runtime/python,路径用相对)
ssh root@<IP> '
cd /home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/runtime/python && \
python3 main.py \
--task detect \
--model-path ../../model/best_bayese_640x640_nv12.bin \
--test-img ../../test_data/test.jpg \
--label-file ../../test_data/custom_classes.names \
--img-save-path ../../test_data/result.jpg \
--classes-num 3 \
--reg 16 \
--strides 8,16,32 \
--score-thres 0.25 \
--nms-thres 0.7'
# 4) 拉回结果验证
scp root@<IP>:/home/root/inference/rdk_model_zoo/samples/vision/ultralytics_yolo/test_data/result.jpg ./
实测性能基线(YOLOv11s @ 640,RDK X5):
- Load Model: 310~345 ms(一次性)
- Pre-process (letterbox + BGR→NV12): 7~50 ms
- Forward (BPU): 18~22 ms ✅
- Post-process: 4~10 ms
- 单帧端到端:~35-90 ms
阶段 5:TROS HobotDnn ROS2(板端 SSH 档位下 Agent 自动跑)
5.1 配置文件(本地生成 → scp 到板端 /home/root/hobot_ws/config/)
custom_workconfig.json:
{
"model_file": "config/best_bayese_640x640_nv12.bin",
"task_num": 4,
"dnn_Parser": "ultralytics_yolo",
"model_output_count": 6,
"reg_max": 16,
"class_num": 3,
"cls_names_list": "config/custom.list",
"strides": [8, 16, 32],
"score_threshold": 0.25,
"nms_threshold": 0.7,
"nms_top_k": 300,
"output_order": [0, 1, 2, 3, 4, 5]
}
(YOLO26 改 "reg_max": 1)
⚠️ 不要照抄 /opt/tros/humble/lib/dnn_node_example/config/yolov11workconfig.json — 官方默认 "dnn_Parser": "yolov8" 是给厂内 modified 模型用的,用户自己量化的模型必须改 "ultralytics_yolo" 否则全错检。
custom.list(行数必须等于 class_num):
park
qrcode
obstacle
5.2 三种验证模式(按推荐顺序)
| 模式 | 输入源 | 用途 | hbmem topic | 优先级 |
|---|---|---|---|---|
| A1 | USB 摄像头 (hobot_usb_cam) | 生产首选,真实视频流 | /hbmem_img ✅ 默认对齐 | ⭐⭐⭐ |
| A2 | MIPI 摄像头 (mipi_cam) | 生产首选(板载相机) | /hbmem_img ✅ 默认对齐 | ⭐⭐⭐ |
| B | hobot_image_publisher 循环图 | 没摄像头时的 backup | 默认 /test_msg ⚠️ 要改名 | ⭐⭐ |
| C | feed_type=0 单图回灌 | 最快调试,验证 bin + parser | 不走话题 | ⭐ |
选型决策:
- 上摄像头 → A1/A2(生产真实场景)
- 没摄像头但要验证 ROS2 全链路 → B
- 只想看 bin 模型对不对 → C(最简单,跳过整个 publisher)
模式 A1:USB 摄像头链路(hobot_usb_cam)
# 后台 1: 启动 USB 摄像头节点(默认发到 /hbmem_img,与 dnn_node 默认订阅完美对齐)
nohup ros2 launch hobot_usb_cam hobot_usb_cam.launch.py \
usb_video_device:=/dev/video8 \
usb_image_width:=640 usb_image_height:=480 \
usb_pixel_format:=mjpeg \
usb_framerate:=30 \
usb_zero_copy:=True > usb_cam.log 2>&1 &
# 后台 2: dnn_node(无需任何 topic remap,默认订阅 /hbmem_img)
nohup bash -c "cd /home/root/hobot_ws && \
ros2 run dnn_node_example example --ros-args \
-p feed_type:=1 -p is_shared_mem_sub:=1 \
-p config_file:=config/custom_workconfig.json" > dnn.log 2>&1 &
# 验证
ros2 topic hz /hobot_dnn_detection # 期望 ~ 30 Hz(瓶颈是 BPU 推理 ~15ms)
ros2 topic echo --once /hobot_dnn_detection
USB 关键参数:
usb_video_device:默认/dev/video8,不一定准!先ls /dev/video*看实际节点(常见是/dev/video0)usb_pixel_format:mjpeg(推荐,带宽小)/yuyv/uyvy,看摄像头硬件支持usb_zero_copy:=True:生产用 True 走 hbmem,False 会走 ROS2 普通 transport(CPU 拷贝多)
模式 A2:MIPI 摄像头链路(mipi_cam)
# 板端常见 sensor: F37 / GC4663 / IMX415 / OV5647
nohup ros2 launch mipi_cam mipi_cam_640x480_nv12_hbmem.launch.py \
mipi_video_device:=F37 > mipi_cam.log 2>&1 &
# 或 1920x1080: ros2 launch mipi_cam mipi_cam_1920x1080_nv12_hbmem.launch.py
# dnn_node(同 A1,无需改话题)
nohup bash -c "cd /home/root/hobot_ws && \
ros2 run dnn_node_example example --ros-args \
-p feed_type:=1 -p is_shared_mem_sub:=1 \
-p config_file:=config/custom_workconfig.json" > dnn.log 2>&1 &
ros2 topic hz /hobot_dnn_detection
MIPI 关键参数:
mipi_video_device:sensor 型号字符串。常见F37,GC4663,IMX415,OV5647,OV13855。型号错了节点直接 die(cap capture init failure)mipi_out_format:nv12(BPU 直吃,最优)/bgr8mipi_io_method:shared_mem(hbmem 零拷贝,强烈推荐)/ros- 不知道 sensor 型号:看板载贴标,或
dmesg | grep -i sensor
模式 B:image_publisher 循环图片(无摄像头 backup)
# 后台 1:图像源 → /hbmem_img(10 fps 循环)⚠️ 必须显式 publish_message_topic_name:=/hbmem_img
nohup ros2 launch hobot_image_publisher hobot_image_publisher.launch.py \
publish_image_source:=/home/root/hobot_ws/config/test.jpg \
publish_image_format:=jpg \
publish_source_image_w:=640 publish_source_image_h:=480 \
publish_fps:=10 publish_is_loop:=True \
publish_message_topic_name:=/hbmem_img \
publish_is_shared_mem:=True > pub.log 2>&1 &
# 后台 2:dnn_node(同 A1)
nohup bash -c "cd /home/root/hobot_ws && \
ros2 run dnn_node_example example --ros-args \
-p feed_type:=1 -p is_shared_mem_sub:=1 \
-p config_file:=config/custom_workconfig.json" > dnn.log 2>&1 &
ros2 topic hz /hobot_dnn_detection # 期望 ≈ publisher fps (10 Hz)
ros2 topic echo --once /hobot_dnn_detection
⚠️ image_publisher 默认 msg_pub_topic_name 是 /test_msg(不是 /hbmem_img),必须显式改名才能和 dnn_node 默认订阅对接。摄像头模式 A 没这问题(cam 节点本身就发 /hbmem_img)。
模式 C:feed_type=0 单图回灌(最快调试)
ssh root@<IP> '
source /opt/tros/humble/setup.bash && cd /home/root/hobot_ws && \
ros2 run dnn_node_example example --ros-args \
-p feed_type:=0 \
-p config_file:=config/custom_workconfig.json \
-p image:=config/test.jpg \
-p image_type:=0 \
-p dump_render_img:=1'
# 输出渲染文件:render_feedback_0_0.jpeg (不是 result.jpg)
# 不需要起 publisher,节点跑完一张图就停,最适合验证 bin/parser 配置
通用注意事项(所有模式)
⚠️ 板端默认没 tmux,用 nohup ... > log 2>&1 & 跑后台节点;清理用:
pkill -f hobot_usb_cam # USB cam
pkill -f mipi_cam # MIPI cam
pkill -f hobot_image_pub # image_publisher
pkill -f "dnn_node_example/example" # dnn_node
⚠️ /dev/hbmem* 设备权限:默认 root 才能写 hbmem。非 root 用户需 sudo chmod 666 /dev/hbmem* 或加入对应 group。
⚠️ topic 名对齐总览:
| 输入源节点 | hbmem 默认 topic | 需改名? |
|---|---|---|
| hobot_usb_cam | /hbmem_img | ❌ 默认对齐 |
| mipi_cam (默认 launch) | /hbmem_img | ❌ 默认对齐 |
| hobot_image_publisher | /test_msg | ✅ 必须改 |
| dnn_node_example 订阅端 | /hbmem_img | — |
5.3 真实性能基准(实测)
YOLOv11s + 自定义 3 类 + 640x640 + RDK X5(OS 3.5.0-beta / dnn_node 2.6.1 / hbmem 零拷贝):
| Pipeline 阶段 | 耗时/帧 | |---|---| | recv image (hbmem 零拷贝) | 9 ms | | preprocess (resize → NV12) | 2 ms | | BPU infer | 15 ms | | postprocess (NMS + 反量化) | 1 ms | | TOTAL pipeline | 29 ms | | 实测帧率 | 9.95 Hz(publisher fps=10 满吃)|
消息类型:ai_msgs/msg/PerceptionTargets(含 rect、type、confidence、track_id),下游可订阅做决策。
Checkpoint
| 阶段 | 验证 | 标准 |
|------|------|------|
| 板端预检 | cat /etc/version + dnn_node 版本 + /dev/bpu* | OS ≥ 3.5.0,dnn_node ≥ 2.6.1,BPU 设备存在 |
| 0 环境 | hb_mapper --version + python -c "import pkg_resources" | 1.24.x,且 setuptools 已装 |
| 1 数据 | 标签数 = 图像数;data.yaml 用绝对 path | 通过 |
| 2 训练 | yolo val mAP50 + ~/.config/Ultralytics/Arial.ttf 占位存在 | mAP50 > 0.7 |
| 3 量化 | hb_mapper 日志 cosine sim + *_bayese_640x640_nv12.bin | 每个 output cosine ≥ 0.99 |
| 4 板端推理 | Python main.py 推理结果图 | 无报错,Forward < 30ms,框正确 |
| 5A 单图回灌 | dump_render_img:=1 输出 render_feedback_0_0.jpeg | 类别正确 |
| 5B ROS2 流 | ros2 topic hz /hobot_dnn_detection + topic echo --once | hz ≈ publisher fps,targets 非空 |
常见问题
import hbm_runtime失败 → 镜像 < 3.5.0- 量化时
ModuleNotFoundError: No module named 'pkg_resources'→pip install setuptools(rdkx5-yolo-mapper 漏依赖) CondaToSNonInteractiveError→ conda 26+ 必须先conda tos accept --override-channels --channel <repo>- pip 装 ultralytics/torch 一直 timeout → 国内换清华源 +
--timeout 60 --retries 10 yolo train卡在Download failure ... Arial.ttf→ 先cp /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf ~/.config/Ultralytics/Arial.ttf- 训练命令
dataset/data.yaml莫名其妙变成datasetta.yaml→ ssh 引号嵌套吞字符,改用ssh ... 'bash -s' < script.sh source /opt/tros/setup.bash找不到 → 用/opt/tros/humble/setup.bashunsupported parser: ultralytics_yolo→ dnn_node < 2.6.1unsupported parser: yolo26_seg→ dnn_node < 2.6.2- HobotDnn 检测全错 → 用
"ultralytics_yolo",不要用"yolov8"(官方 yolov11workconfig.json 默认错的,要改) ImportError: utils→ utils/ 目录未传,或目录深度不对(必须 5 层)- 自定义分辨率推理错位 → 必须 H=W 且能被 32 整除
- SSH 断开训练中断 → 必须用 tmux/nohup
- 板端没 tmux →
apt install tmux或直接nohup ... > log 2>&1 & class_names length X is not equal to class_num Y→ cls_names_list 行数 ≠ class_num- topic list 都有
/hbmem_img和/hobot_dnn_detection但topic hz没输出 → publisher 和 dnn_node 的话题名没对齐(默认一个/test_msg一个/hbmem_img) scp src1 src2 host:dst报No such file or directory→ 新 OpenSSH(≥ 9.0)行为变了,逐个 scp 或scp -r- WSL2 找不到
nvidia-smi→ 它在/usr/lib/wsl/lib/nvidia-smi,加进 PATH 即可
参考
- 官方 YOLO ROS 文档: https://developer.d-robotics.cc/rdk_doc/Robot_development/boxs/detection/yolo
- ultralytics_yolo: https://github.com/D-Robotics/rdk_model_zoo/tree/rdk_x5/samples/vision/ultralytics_yolo
- ultralytics_yolo26: https://github.com/D-Robotics/rdk_model_zoo/tree/rdk_x5/samples/vision/ultralytics_yolo26
- HobotDnn CHANGELOG: https://github.com/D-Robotics/hobot_dnn/blob/develop/dnn_node/CHANGELOG.md
微信扫一扫