☰
算能BM1684升级BM1688实战:tpu_mlir模型转换与SDK更新后的代码调试指南
2026/10/1 15:12:59 网站建设 项目流程

1. 从 BM1684 到 BM1688:一次真实的迁移踩坑记录

算能 BM1684 升级 BM1688 这件事,我原本以为只是把--chip bm1684改成--chip bm1688就完事了。结果从 tpu_mlir 模型转换到 SDK 更新,再到工程代码编译调试,前前后后折腾了快一周。这篇文章把整个迁移过程拆成可复制的步骤,重点讲清楚 BM1684 迁移 BM1688 时 tpu_mlir 转换参数怎么改、SDK 头文件库文件怎么对齐、以及升级后代码调试最容易卡住的几个报错。

先说清楚这套流程适合谁:如果你手上已经有跑在 BM1684 上的 YOLOv5 或其他检测模型工程,现在要迁到 BM1688(CV186AH)平台,并且用的是算能官方 sophonsdk_edge 系列 SDK + tpu_mlir 工具链,那这篇基本能覆盖你 80% 的坑。核心检索词就三个:BM1684、BM1688、tpu_mlir,围绕它们展开。

迁移的整体链路是这样的:旧平台 BM1684 上你有一套能跑的 onnx 模型和 C++ 推理工程;新平台 BM1688 需要重新用 tpu_mlir 把 onnx 转成 bmodel,同时 SDK 从旧版本升到 v1.9 系列,头文件和库文件路径全变了,工程里的 Makefile、CMakeLists、后处理代码都得跟着改。任何一环没对齐,表现就是编译报错或者模型跑起来一个框都检测不到。

我这次迁移的起点是一个 YOLOv5s 的 PCB 缺陷检测模型,四个输出头,之前在 BM1684 上跑得好好的。下面按实际操作顺序来,每一步都给可复制的命令。

2. 前置准备:SDK 下载、Docker 镜像与 tpu_mlir 安装

2.1 SDK 下载与目录结构

先去算能技术资料页面下载 sophonsdk_edge 的官方 release 包,我这次用的是sophonsdk_edge_v1.9_official_release。解压之后重点看两个目录:tpu_mlir(模型转换工具链)和sophon-img(板端运行时库和头文件)。这两个目录后面会反复用到。

这里有个容易忽略的点:BM1684 时代你可能用的是Release_v2312-LTS那套 tpu-mlir,版本号是tpu-mlir_20231116。BM1688 必须换到 v1.9 配套的 tpu_mlir,不能混用。混用的直接后果就是转换出来的 bmodel 在板端加载失败,或者精度对不上。

2.2 Docker 镜像拉取与标签管理

以前转 BM1684 模型时,拉镜像命令是:

docker pull sophgo/tpuc_dev:latest

现在转 BM1688,官方给的命令还是sophgo/tpuc_dev:latest。问题来了:latest 标签会覆盖,你本地原来的 BM1684 环境就没了。我的做法是先把旧的 latest 重命名保留:

docker tag sophgo/tpuc_dev:latest sophgo/tpuc_dev:bm1684

然后再拉新的 latest,并打上 bm1688 标签:

docker pull sophgo/tpuc_dev:latest docker tag sophgo/tpuc_dev:latest sophgo/tpuc_dev:bm1688

这样两个环境互不干扰,随时可以切回去验证 BM1684 的老模型。

2.3 创建容器与安装 tpu_mlir

创建 BM1688 专用容器,把当前目录挂进 /workspace:

docker run --privileged --name bm1688 -v $PWD:/workspace -it sophgo/tpuc_dev:bm1688

进容器后装 tpu_mlir。官方源下载慢,加清华源:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tpu_mlir[all] pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tpu_mlir[onnx,torch]

装完进 tpu_mlir 目录 source 环境变量:

cd /workspace/sophonsdk_edge_v1.9_official_release/tpu_mlir source envsetup.sh

这一步做完,model_transform.py、run_calibration.py、model_deploy.py这些命令才能直接用。

2.4 工作目录准备

建一个模型工作目录,把 onnx 和校准图片放进去:

mkdir -p model_yolov5s/workspace cd model_yolov5s # 把 onnx 模型和 calib 图片目录拷进来

我图省事,直接把 BM1684 时代的整个model_yolov5s目录拷过来,然后清空 workspace 里的中间文件:

cp -drf /data/chw/.../model_yolov5s /data/chw/.../tpu_mlir/model_yolov5s rm -rf model_yolov5s/workspace/*

这样 onnx、calib 图片、coco.names 都还在,只清掉旧的 mlir 和 bmodel,避免新旧文件混淆。

3. tpu_mlir 模型转换:onnx 转 mlir 与 int8 量化配置

3.1 onnx 转 mlir

在model_yolov5s/workspace目录下执行:

model_transform.py \ --model_name yolov5s \ --model_def ../edge_compute_best_20230821.onnx \ --input_shapes [[1,3,640,640]] \ --mean 0.0,0.0,0.0 \ --scale 0.0039216,0.0039216,0.0039216 \ --keep_aspect_ratio \ --pixel_format rgb \ --output_names 339,391,443 \ --test_input ../calib/00ca3e9473b4407bb1e72a38a7c6c69f.jpg \ --test_result yolov5s_top_outputs.npz \ --mlir yolov5s.mlir

--output_names这三个名字必须用 Netron 打开你的 onnx 确认,不同训练脚本导出的节点名不一样。这一步如果名字写错,转换不会报错,但后面量化出来的模型输出维度会不对。

3.2 生成校准表

run_calibration.py yolov5s.mlir \ --dataset ../calib \ --input_num 200 \ -o yolov5s_cali_table

calib 目录里放 200 张左右的代表性图片。数量太少校准表不准,int8 掉点严重;太多则转换时间线性增长。200 张是个比较稳的平衡点。

3.3 编译 int8 量化模型

model_deploy.py \ --mlir yolov5s.mlir \ --quantize INT8 \ --calibration_table yolov5s_cali_table \ --chip bm1688 \ --test_input yolov5s_in_f32.npz \ --test_reference yolov5s_top_outputs.npz \ --tolerance 0.85,0.45 \ --model yolov5s_pcb_4shuchu_1688_int8_sym.bmodel

关键改动就一处:--chip bm1688。BM1684 时代这里是--chip bm1684。tolerance 参数如果量化后精度掉得厉害,可以适当放宽,但别放太松,否则掩盖真实问题。

3.4 混精度配置

如果 int8 精度不达标,需要混精度。混精度不能照抄 BM1684 的配置,芯片名、层名都要改。核心思路是找出量化敏感层,在model_deploy.py里通过--quantize配合混合精度配置文件指定哪些层走 fp32。具体哪些层敏感,用--debug输出每层余弦相似度,挑相似度低的层。

3.5 转换参数对照表

参数BM1684 旧值BM1688 新值说明
--chipbm1684bm1688必改
--quantizeINT8INT8不变
--tolerance0.85,0.450.85,0.45视精度调整
--output_names旧节点名新节点名需 Netron 确认
tpu_mlir 版本20231116v1.9 配套不可混用

4. SDK 更新后的库文件头文件对齐与编译验证

4.1 拷贝 libsophon 库和头文件

BM1688 的 SDK 版本是libsophon_soc_0.4.11_aarch64。在板子上建目录,从编译机 scp 过来:

mkdir -p /data1/chw/bitmain_all_1688/lib/sophon cd /data1/chw/bitmain_all_1688/lib/sophon scp -rf root@192.168.1.10:/data/chw/.../libsophon_soc_0.4.11_aarch64/opt/sophon/libsophon-0.4.11/lib/* ./

头文件同理:

mkdir -p /data1/chw/bitmain_all_1688/include/sophon scp -r root@192.168.1.10:/data/chw/.../libsophon-0.4.11/include/* ./

4.2 拷贝 ffmpeg 和 opencv 库头文件

先把 BM1684 时代的旧 ffmpeg、opencv 头文件和库全删掉,避免新旧混用导致链接到错误符号。然后从sophon-media-soc_1.9.0_aarch64拷贝:

scp -r root@192.168.1.10:/data/chw/.../sophon-ffmpeg_1.9.0/lib/* /data1/chw/bitmain_all_1688/lib/ffmpeg/ scp -r root@192.168.1.10:/data/chw/.../sophon-ffmpeg_1.9.0/include/* /data1/chw/bitmain_all_1688/include/ffmpeg/ scp -r root@192.168.1.10:/data/chw/.../sophon-opencv_1.9.0/include/* /data1/chw/bitmain_all_1688/include/opencv/ scp -r root@192.168.1.10:/data/chw/.../sophon-opencv_1.9.0/lib/* /data1/chw/bitmain_all_1688/lib/opencv/

4.3 修改 Makefile 路径

因为删了旧文件夹、建了新文件夹,Makefile 里的 include 和 lib 路径必须同步改。重点检查-I和-L两处,确保指向bitmain_all_1688而不是旧的bitmain_all_1684。

4.4 编译报错逐项排查

直接 make,看报错一个个解决。

报错一:fatal error: bmnn_utils.h: No such file or directory

这个头文件在算能模型 demo 的 dependencies 目录里,不在标准 SDK include 里。把它拷到include/sophon下即可。

报错二:error: 'string' is not a member of 'std'

新 SDK 头文件没有隐式包含<string>,在报错的源文件顶部加:

#include <string>

报错三:error: 'bmcv_padding_atrr_t' was not declared; did you mean 'bmcv_padding_attr_t'?

这是 SDK 更新后改了拼写,旧名atrr改成了attr。解决方式有两种:在公共头文件加typedef bmcv_padding_attr_t bmcv_padding_atrr_t;,或者直接包含bm_wrapper.hpp,里面已经做了兼容定义。我选后者,一行 include 搞定。

报错四:/usr/bin/ld: cannot find -lbmvideo

-lbmvideo、-lbmjpuapi、-lbmjpulite这几个库是 BM1684 时代的,BM1688 SDK 里已经没有了。直接在 Makefile 里把这几个-l去掉。

4.5 编译成功验证

改完上面四处,再 make 应该就能过。编译通过不代表模型能跑对,下一步必须验证推理结果。

5. 常见错误排查:模型跑不出框与后处理对齐

5.1 现象:程序能跑但一个框都没有

编译过了,程序也起来了,但检测结果为空。这时候别急着改代码,先用官方 demo 验证模型本身有没有问题。

把sophon-demo里的 YOLOv5 sample 整个拷到 BM1688 盒子上,直接在盒子上编译(省去交叉编译环境搭建):

mkdir build && cd build cmake -DTARGET_ARCH=soc -DSDK=/opt/sophon/ -DCMAKE_BUILD_TYPE=Debug .. make

注意-DSDK=/opt/sophon/在没装 SDK 的情况下其实不生效,需要在 CMakeLists.txt 里手动把库和头文件路径指向你实际拷贝的目录。

然后跑官方 demo:

./yolov5_bmcv.soc --bmodel=./easnet_pcb_4shuchu_1688_int8_sym.bmodel --input=shigu5.mp4 --classnames=coco.names

如果 demo 能出框,说明模型没问题,问题在你的工程代码。

5.2 根因:输出头选择与 sigmoid 处理不一致

我对比了自己的后处理代码和官方 demo,发现关键差异。官方 demo 在post_process里对输出做了 sigmoid 解码:

dst[0] = (sigmoid(ptr[0]) * 2 - 0.5 + i % feat_w) / feat_w * m_net_w; dst[1] = (sigmoid(ptr[1]) * 2 - 0.5 + i / feat_w) / feat_h * m_net_h; dst[2] = pow((sigmoid(ptr[2]) * 2), 2) * anchors[tidx][anchor_idx][0]; dst[3] = pow((sigmoid(ptr[3]) * 2), 2) * anchors[tidx][anchor_idx][1]; dst[4] = sigmoid(ptr[4]);

而我的代码里没有 sigmoid 处理。原因是我之前 BM1684 转模型时,用的不是那三个分散输出头,而是 concat 之后的汇总输出(1×25200×8),那个输出在模型内部已经做过 sigmoid,所以后处理不需要再算。这次 BM1688 转模型时我用了三个分散输出头,后处理却没加 sigmoid,自然解不出框。

5.3 解决:重新转模型指定汇总输出

两种改法:一是后处理加 sigmoid,二是重新转模型用汇总输出。我选后者,因为改动最小。重新执行model_transform.py,把--output_names改成汇总输出节点名(Netron 里看是output),并且用混合量化:

model_transform.py \ --model_name yolov5s \ --model_def ../edge_compute_best_20230821.onnx \ --input_shapes [[1,3,640,640]] \ --mean 0.0,0.0,0.0 \ --scale 0.0039216,0.0039216,0.0039216 \ --keep_aspect_ratio \ --pixel_format rgb \ --output_names output \ --test_input ../calib/00ca3e9473b4407bb1e72a38a7c6c69f.jpg \ --test_result yolov5s_top_outputs.npz \ --mlir yolov5s.mlir

然后重新走校准和model_deploy.py,chip 仍是 bm1688。转出来的 bmodel 替换进工程,检测框正常出现。

5.4 排查路径总结

现象排查动作结论
编译报错找不到头文件检查 include 路径补拷 bmnn_utils.h
链接报错找不到库检查 Makefile -l 项删 BM1684 专属库
程序跑但无框官方 demo 验证模型模型 OK,代码问题
demo 有框工程无框对比后处理输出头/sigmoid 不一致
宽高异常 2048保存中间图前处理正常,定位后处理

6. 迁移完成后如何继续验证与接入工具链

模型转换和代码调试都通了之后,建议做两件事巩固这次迁移成果。

第一,把 BM1684 和 BM1688 两套 bmodel 放在同一个测试集上跑,对比 mAP 和推理耗时。int8 量化后精度掉 1-2 个点是正常的,如果掉超过 5 个点,回去检查校准表质量和混精度配置。第二,把这次改动的 Makefile、CMakeLists、后处理代码差异整理成 patch,下次再迁其他模型直接套用。

如果你在迁移过程中需要频繁验证模型输出、对比不同量化配置的结果,可以用 TaoToken 的模型对话能力快速做结果比对和参数分析,省去本地写脚本的时间。API 接入地址是 https://taotoken.net/api,模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat。需要管理多个项目的 Key 时,控制台在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=console,API Keys 页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys。

如果你后续要做长期的边缘 AI 模型迭代和 Agent 化部署,Coding Plan 适合把模型转换、代码调试、版本管理串成流水线,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc,Claude Code 相关配置参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claude_code。

最后留一个我踩过的坑:BM1688 的 SDK 头文件里bmcv_padding_attr_t这个拼写改动,如果你工程里有多处引用旧名,别一个个改,直接在公共头文件加 typedef 兼容,省事且不容易漏。模型转换那边,--output_names一定要用 Netron 确认,别凭记忆写,这是最容易导致「模型能转但跑不出结果」的隐形坑。

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

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

立即咨询