来源:
notes/documents/02_dg板卡X5_BPU推理服务_可视化HTML.mddg 板卡 X5 BPU 推理服务:可视化 HTML 报告
日期:2026-09-19
这是一篇原生 Markdown 内嵌 HTML 的笔记:正文本身是一段自包含的 HTML(纯 <style> + <div>,无 JavaScript、无外部资源),嵌在 Markdown 里由博客渲染。它用图表的方式把整套 BPU 推理服务讲了什么、原理是什么、实测到什么,一次性呈现出来。
说明:飞书文档导入会丢弃 <div> / <style> 这类原生 HTML 标签(已实测确认),所以这篇只在博客侧渲染;文字版原理与代码见 notes/documents/01_dg板卡X5_BPU推理服务_原理_问答_代码.md。
dg 板卡 X5 · BPU 推理服务
一台没有摄像头、麦克风、屏幕的地瓜机器人 RDK X5,被我改造成了一台纯算力的 AI 推理服务器:图片从网络进来,检测结果从网络出去。
1我做了什么
把 BPU 变成网络服务
纯标准库 ThreadingHTTPServer,零额外依赖。图片 POST 进来,检测框 / 分类结果 JSON 回去,浏览器还能直接拖图上传。
直接吃板载模型
板子 /opt/hobot/model/x5/basic 下 34 个官方 .bin 模型开箱即用,不需要做任何模型转换。
三种后处理解码器
YOLOv5 锚框解码、YOLOv8 / v10 / v11 / v12 的 DFL 解码、ImageNet 分类 top-k;不认识的模型退回原始张量统计。
为摄像头预留了接口
除了编码图片,还做了 /v1/infer/nv12,直接收原始 NV12 帧。以后接上摄像头或 RTSP 解码器,这层不用改。
2从我的 Python 到 BPU 硬件:完整调用链
from hbm_runtime import HB_HBMRuntime,没有任何路径 hack。libdnn.so,没有 RPATH。hbDNN* C API;往下挂 libcnn_intf / libhbmem(BPU 共享内存)/ libhbrt_bayes(BPU Runtime 3.15.55)。bpu_framework → bpu_cores → bpu_hw_io_x5,通过字符设备 /dev/bpu_core0 (10,84)、/dev/bpu (10,85)、/dev/ion (10,127) 下发任务。3一张 JPEG 的完整数据流
4五个契约点:为什么这样写才能精准命中 BPU
必须是 OpenExplorer 量化编译后的 .bin,BPU 不认识 .pt / ONNX。
input/output 名字必须运行时从 runtime 实例读,不能硬编码。
input_shape 写 [1,3,640,640],实际要喂 614400 字节的 packed NV12。
定点输出必须按逐通道 scale / zero_point 反量化,直接转 float 是错的。
同一 .bin 重复加载会被 runtime 去重,只能单实例 + 信号量 + 多线程 run。
5实测性能
BPU 是单核的:线程数 1 → 4 时吞吐几乎不变(100 → 99.4 FPS),但单帧延迟从 9.8 ms 涨到 30 ms,多出来的线程全在排队。跨 ZeroTier 的差距几乎全部来自网络传输(137 KB 走公网虚拟局域网),不是算力问题。
6两张图的实测结果
bus.jpg · 810×1080 · 官方 COCO 测试图
| 模型 | 主要结果 | 耗时 |
|---|---|---|
| yolo11m | bus 0.93 · person 0.90/0.90/0.89/0.78 | 43.2 ms |
| yolov12n | bus 0.86 · person 0.82/0.78/0.75 | 25.4 ms |
| yolov8 | bus 0.82 · person 0.74/0.69/0.67 | 10.0 ms |
| yolov5s_v6 | person 0.78/0.77/0.72 · bus 0.74 | 20.2 ms |
| mobilenetv2 | 分类 minibus 0.48 | 2.6 ms |
扣篮照 · 1279×1920 竖图
| 设置 | 主要结果 | 耗时 |
|---|---|---|
| yolo11m + stretch | 扣篮者 0.86 · 篮下球员 0.77 · 场边 4 人 0.68/0.56/0.54/0.45 | 38.5 ms |
| yolo11m + letterbox | 最高只有 0.65(竖图被压小) | 38.1 ms |
| mobilenetv2 | 分类 scoreboard 0.54 | 2.4 ms |
| googlenet | scoreboard 0.12 · ballplayer 0.08 | 3.1 ms |
关键教训:竖图必须用 resize=stretch,letterbox 会把它缩成 640×426、上下填黑边,目标置信度从 0.86 掉到 0.65。
7你问过的问题,一次说清
X5 的 BPU 可以拿来做什么?
四类事:目标检测(YOLO 全家族、SSD、FCOS、CenterNet)、图像分类(MobileNet / ResNet / GoogLeNet / EfficientNet)、语义分割(DeepLabv3+、STDC)、多模态大模型(CLIP 文本编码、LLaMA.cpp)。核心优势是比 CPU 高一个量级的算力、只有几瓦功耗。
没有摄像头、麦克风、扩音器,还能做什么?
实测确认 /dev/video* 不存在、采集到的音频是饱和噪声、也没有显示设备,所以"感知输入"这条腿断了。但可以当纯算力服务器:网络推理服务、离线批量处理、边缘计算节点,以及 GPIO/I2C/SPI/UART 的传感器控制类项目。
代码是怎么精准调用到 BPU 的?
不是猜路径,是分层调用 + 五个契约点:Python 只 import 官方 hbm_runtime,它用 pybind11 包住 libdnn.so,再往下是 hbrt、内核模块、/dev/bpu_core0。真正难的不是调用,是让输入排布、张量名、量化参数都符合运行时约定。
是地平线封装了官方 Python 库吗?
是。hobot-dnn 提供底层 libdnn / libhbrt;hobot-spdev 提供 Python wheel(hbm_runtime-3.0.9-py3-none-any.whl),安装时由 postinst 自动 pip3 install 到 dist-packages。不是我写的,也不是第三方。
代码怎么找到官方封装的库的?
没有任何路径 hack。Python 层靠 dist-packages 本来就在 sys.path 里;原生库层靠 /etc/ld.so.conf.d/ 下地平线装的配置 + ldconfig 缓存,而 HB_HBMRuntime.so 自己不写 RPATH。我代码里只有一行懒加载 import。
代码在哪里?量大吗?
开发副本在 dg-bpu-service/,部署在 dg:/opt/bpu-service/,systemd 托管。服务本体 1430 行,加测试与工具共 1731 行;没有 Web 框架、没有数据库、没有前端构建链。
8踩过的坑
结论
X5 的 BPU 在没有摄像头、麦克风、屏幕的情况下,依然可以作为一个高性价比的边缘 AI 算力节点:图片进、结果出。整套服务 1430 行纯标准库 Python,把 CPU(预处理 + 后处理)和 BPU(前向推理)的分工划清楚,就能稳定跑出 yolov8 单帧 9.8 ms、端到端 52 ms 的成绩。真正的门槛不在"调用 BPU",而在理解它的五个数据契约。
附:原生 Markdown 嵌入 HTML 的实测结论
这次实验的目标是搞清楚「怎么把 HTML 放进 Markdown 里,并真的让别人看到」。
做法:把一整段自包含 HTML(<style> + <div>,无 JavaScript、无外部图片/字体/CDN)直接写在 Markdown 正文里。块级 HTML 标签必须顶格起行,中间不要用 Markdown 语法包裹它。
结论一:博客侧可以渲染(可用)。
博客是 Jekyll,_config.yml 里 markdown: kramdown + kramdown.input: GFM。kramdown 在 GFM 模式下会把块级原生 HTML 原样透传,不做转义。导入脚本 import_personal_assistant_notes.py 也只是把笔记原文照搬进 _posts/,没有任何 HTML 转义逻辑,所以内嵌 HTML 能一路走到最终页面。
结论二:飞书文档侧不能渲染(会丢)。
lark-cli docs +create --doc-format markdown 走的是飞书的 Markdown → 文档块转换,实测结果是:
| 写法 | 飞书导入后 |
|---|---|
<div style="...">...</div> |
标签被丢弃,只剩里面的纯文本 |
<b> / <p> |
被当作飞书 XML 富文本节点处理 |
<style>.x{...}</style> |
样式被当成普通文字原样保留(很难看) |
<table><tr><td> |
被识别并转成 Markdown 表格 |
所以同一篇笔记里混大量原生 HTML,会让飞书那边的文档变得很乱。这次的做法是拆成两篇:文字版走飞书 + 博客,HTML 可视化版只在博客。
结论三:Jekyll 的 Liquid 是隐藏地雷。 Jekyll 在 Markdown 渲染之前会先跑一遍 Liquid 模板引擎,所以正文里任何成对的双花括号(Liquid 的输出语法)或「花括号 + 百分号」(Liquid 的标签语法)都会在渲染前被当成模板求值,轻则内容消失、重则整站构建失败。写代码示例(尤其是嵌套字典字面量)或 CSS 时特别容易踩到。规避方式有两种:一是内容里干脆不出现这两个序列;二是用 Liquid 自带的 raw / endraw 标签把整段包起来,标签只对博客侧生效,飞书侧会看到标签本身。
结论四:必须无 JavaScript。
kramdown 对 <script> 的处理不稳定,而且博客正文里塞脚本既没必要也不安全。这次的可视化全部用纯 CSS 实现:分层图用带色边框的卡片 + <div>▼</div>,条形图用百分比宽度的 <i>,响应式用 @media。这样任何 Markdown 渲染器都不会把它拆坏。
可复用的模板骨架:
<style>
.viz-wrap{ /* 所有样式必须自带命名空间前缀,避免和博客主题冲突 */ }
</style>
<div class="viz-wrap" markdown="0">
<h3>标题</h3>
<p>正文……</p>
</div>
三个要点:样式类名加统一前缀(这次用 bpuv-)、markdown="0" 提示渲染器不要解析内部内容、整段 HTML 里不要出现成对的双花括号或 Liquid 标签语法。