Back to skills
extension
Category: OtherNo API key required

RDK_YOLO_Develop

端到端 YOLO 训练 → 地瓜 RDK X5 BPU 量化部署:Monkey Patch ONNX 导出 → mapper.py 量化 → 板端 hbm_runtime 推理 → TROS HobotDnn ROS2 实时检测。支持 YOLOv5u~YOLOv13 和 YOLO26 全系列。

personAuthor: user_76096a1ehubcommunity

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 节点)必须用 tmuxnohup 防 SSH 断开
  • 跨 SSH 传脚本永远走 ssh ... 'bash -s' < local_script.sh,不要把长命令塞进 ssh ... "..." — 引号嵌套会被随机吞字符(实测:pip install 被切成 pip iuietdataset/data.yaml 被切成 datasetta.yaml,找半天 bug)
  • scp 多源限制:新 OpenSSH(≥ 9.0)scp src1 src2 host:dst 行为变了,建议逐个 scpscp -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_yolo parser
  • tros_2.6.2 (2026-05-08):yolo26_seg parser

实测基线(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) |


七大易错点(真机踩坑总结)

  1. dnn_Parser:用户量化的模型(box 含反量化节点)一律用 "ultralytics_yolo"不要用 "yolov8" ⚠️ 官方 /opt/tros/humble/lib/dnn_node_example/config/yolov11workconfig.json 默认是 yolov8,会完全错检,复制后必须改
  2. utils/ 必传main.py 用相对路径导入 utils/py_utils/,且需要保持 samples/vision/<model>/runtime/python/ 这种 5 层深度(脚本里有 sys.path.append("../../../../../")
  3. export_monkey_patch.py 用 simplify=False:不要改成 True,会破坏 BPU 优化路径
  4. rdkx5-yolo-mapper 隐性依赖 setuptools:新版 conda env 不带 pkg_resources,量化会在 horizon_nn.build_onnx 阶段抛 ModuleNotFoundError: No module named 'pkg_resources'。装包后必须 pip install setuptools
  5. conda 26+ 必须先接受 ToSconda 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
  6. pip 国内网络:torch / ultralytics 直连 pypi 常超时挂死。国内必须换清华源 -i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 60 --retries 10
  7. 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 远程训练(推荐做法)

  1. 本地 Write 一个 run_train.sh(含 conda activate + yolo 命令)
  2. 远端 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.pysys.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_formatmjpeg(推荐,带宽小)/ 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型号错了节点直接 diecap capture init failure
  • mipi_out_formatnv12(BPU 直吃,最优)/ bgr8
  • mipi_io_methodshared_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.bash
  • unsupported parser: ultralytics_yolo → dnn_node < 2.6.1
  • unsupported 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_detectiontopic hz 没输出 → publisher 和 dnn_node 的话题名没对齐(默认一个 /test_msg 一个 /hbmem_img
  • scp src1 src2 host:dstNo 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