# 昇腾 NPU 模型转换指南 > 硬件: Atlas 200I DK2 / 昇腾 310B4 | CANN 6.2.RC2 | YOLOv8n --- ## 方案概要 ``` YOLOv8n.pt → YOLOv8n.onnx → yolov8n.om (ACL原生推理) ``` | 步骤 | 工具 | 耗时 | 资源需求 | |------|------|------|----------| | 1. 下载权重 | ultralytics | ~1min | 无 | | 2. 导出 ONNX | ultralytics | ~85s | 需安装 onnx/onnxslim | | 3. ATC 转换 OM | CANN atc | ~16min | 3GB RAM + swap, 单核编译 | ## 环境要求 ``` Ubuntu 22.04 aarch64 Python 3.9+ (miniconda) CANN 6.2.RC2 (已安装于 /usr/local/Ascend/ascend-toolkit/latest) Go 1.22+ (编译 edge-agent) ``` ## 详细步骤 ### 第 1 步:安装依赖 ```bash # Python 依赖 pip3 install "numpy>=1.24.0,<2.0.0" pip3 install ultralytics opencv-python-headless # Go (系统源版本太低, 需手动安装) wget https://go.dev/dl/go1.22.5.linux-arm64.tar.gz rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.5.linux-arm64.tar.gz rm go1.22.5.linux-arm64.tar.gz export PATH=/usr/local/go/bin:$PATH # 验证 python3 -c "import ultralytics; print('OK')" go version # 应输出 go1.22.5 ``` ### 第 2 步:导出 ONNX ```bash cd /path/to/AI-tianyan python3 -c " from ultralytics import YOLO model = YOLO('yolov8n.pt') # 或自定义模型路径 model.export(format='onnx', imgsz=640, dynamic=False, opset=11, simplify=True) " ``` 输出:`yolov8n.onnx` (约 13MB) **注意事项:** - `dynamic=False`:静态输入尺寸,ATC 转换必须 - `opset=11`:CANN 6.2 最稳定的 ONNX opset 版本 - `simplify=True`:使用 onnxslim 简化模型图 ### 第 3 步:ATC 转换 OM(关键步骤) ```bash # 设置环境变量(3GB RAM 设备必须) export TBE_IMPL_MODE=AI_CORE # 使用 AI Core 模式 export TE_PARALLEL_COMPILER=1 # 单线程编译,避免内存溢出 # 执行转换 atc \ --model=yolov8n.onnx \ --framework=5 \ # 5 = ONNX --output=yolov8n \ --soc_version=Ascend310B4 \ # 昇腾 310B4 --input_format=NCHW \ --input_shape="images:1,3,640,640" \ --output_type=FP16 # FP16 更快,精度够用 # 约 16 分钟后得到 yolov8n.om (约 7.1MB) ``` **常见失败原因:** | 错误 | 原因 | 解决 | |------|------|------| | EOFError / 进程崩溃 | TBE 多线程编译 OOM | 设置 `TE_PARALLEL_COMPILER=1` | | 输出全零 | `bytes_to_ptr()` 后 bytes 被 GC | 保持 bytes 引用见 infer_server.py | | 算子不支持 | ONNX opset 版本过高 | 改用 `opset=11` | ### 第 4 步:部署模型 ```bash # 复制模型到指定位置 cp yolov8n.om /opt/tianyan-edge/model/model.om # 更新类别文件(COCO 80 类) cp model/names.txt /opt/tianyan-edge/model/names.txt # 启动服务 cd /opt/tianyan-edge python3 python/infer_server.py # 推理服务 ./bin/edge-agent -config config/edge.yaml # 主控 Agent ``` --- ## 其他模型 | 模型 | 导出命令 | 输出尺寸 | |------|---------|---------| | yolov8s | `YOLO('yolov8s.pt').export(...)` | 22.5MB onnx → 11MB om | | yolov8m | `YOLO('yolov8m.pt').export(...)` | 51.9MB onnx → 25MB om | | yolov8l | `YOLO('yolov8l.pt').export(...)` | 88.3MB onnx → 43MB om | | yolov8n | `YOLO('yolov8n.pt').export(...)` | 12.3MB onnx → 7.1MB om | **注意:** 大模型转换需要更多内存,建议 8GB+ RAM。 --- ## YOLOv10 注意事项 如果用 YOLOv10,需要: 1. `pip install ultralytics yolov10` 2. 导出:`YOLOv10('yolov10n.pt').export(format='onnx', ...)` 3. 修改 `OUTPUT_FORMAT=nms_free` (YOLOv10 输出格式不同) 4. ATC 命令完全相同 --- ## 已知 Bug 修复 ### ACL bytes_to_ptr 提前 GC 问题 **问题描述:** `acl.util.bytes_to_ptr(blob.tobytes())` 返回的指针, 由于 `blob.tobytes()` 创建的临时 bytes 对象被 Python GC 回收, 导致后续 `acl.rt.memcpy` 读到全零数据。 **修复方案(见 `python/infer_server.py`):** ```python # 错误写法 - bytes 临时对象会被 GC host_addr = acl.util.bytes_to_ptr(blob.tobytes()) acl.rt.memcpy(in_buf, in_sz, host_addr, in_sz, ...) # 读到空数据 # 正确写法 - 保持引用 host_bytes = blob.tobytes() # 保持引用 host_addr = acl.util.bytes_to_ptr(host_bytes) acl.rt.memcpy(in_buf, in_sz, host_addr, in_sz, ...) # 数据正确 ``` ### FP16 输出解析 ATC 转换时指定 `--output_type=FP16` 后,模型输出为 half precision。 需要按半精度读取并转为 float32: ```python host_bytes = host.tobytes() acl.rt.memcpy(acl.util.bytes_to_ptr(host_bytes), sz, out_buf, sz, ...) host_from_bytes = np.frombuffer(host_bytes, dtype=np.uint8) out_fp16 = host_from_bytes.view(np.float16) output = out_fp16.astype(np.float32).reshape(shape) ```