问题:

(1)nncase无法识别中文路径

(2)模型骨干结构是自定义的,必须对应的版本

(3)已有虚拟环境

一,.pt转.onnx

.pt是yolo的权重文件,通过训练后再自动生成文件夹的weights文件夹下下有best.pt文件。best.pt文件是训练生成的文件,可以通过"export"函数转为best.onnx文件。(需要下载ultralytics,当然能够训练出了best.pt文件说明已经下载好了)

示例:

from ultralytics import YOLO

# 加载 YOLOv8 模型(替换成你的 .pt 模型路径)
model = YOLO("C:/Users/Desktop/pipe_detection_v1/train/weights/best.pt")

  # 若你的模型是自定义的,填绝对路径如 "C:\\Users\\Desktop\\YOLOv8-yan\\best.pt"

# 导出 ONNX 格式
model.export(
    format="onnx",
    opset=11,  # Tengine 兼容的 opset 版本
    imgsz=320,  # 输入尺寸,和后续量化/推理一致
    simplify=True,  # 简化模型,去除冗余节点
    int8=True  # 量化模型,提升推理速度
)

print("✅ YOLOv8 .pt → ONNX 转换完成!")

export中的参数决定导出模型的检测效果精度等等,使用 Ultralytics YOLO 进行模型导出 | Ultralytics 文档中有该函数中参数的使用详细教程。

二,.onnx转.kmodel

我遇到了以下问题:

(1)我创建了虚拟环境隔离外部配置,模型中存在骨干是自定义的,因此导出模型也必须复用原来的虚拟环境,保证下载的配置版本一致,且能让它“看懂”自定义骨干。

(2)nncase/.NET在中文路径下不稳定,可能会报错,针对包含中文路径的虚拟环境,需要做一个别名入口

目标:把C:\Users\中文路径\Desktop\pipe_detection_v1\train\weights\best.onnx导出成 K230 可用的 .kmodel

1.给虚拟环境等等加英文路径确保nncase能识别

cmd /c mklink /J C:\k230_env "C:\Users\中文路径\source\code_file\yolonew_env"
  cmd /c mklink /J C:\k230_proj "C:\Users\中文路径\Desktop\YOLOv8-yan"
  cmd /c mklink /J C:\k230_pipe "C:\Users\中文路径\Desktop\pipe_detection_v1"

作用分别是(解决nncase无法识别中文路径问题,注意修改路径):

  - C:\k230_env:现有虚拟环境的英文入口
  - C:\k230_proj:项目目录的英文入口
  - C:\k230_pipe:模型目录的英文入口

2.下载需要的文件

(1)Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "C:\Users\中文路径\Desktop\YOLOv8-yan\convert_ascii\dotnet-install.ps1"

作用:.NET SDK 自动安装脚本,用于部署 .NET 运行环境(从官方地址下载的)

(2)Invoke-WebRequest -Uri "https://github.com/kendryte/nncase/releases/download/v2.11.0/nncase-2.11.0-cp39-cp39-win_amd64.whl" -OutFile "C:\Users\中文路径\Desktop\YOLOv8-yan\convert_ascii\nncase-2.11.0-cp39-cp39-win_amd64.whl"

作用:nncase 核心库(Python 3.9 Windows 专用),模型转换核心依赖

(3)Invoke-WebRequest -Uri "https://github.com/kendryte/nncase/releases/download/v2.11.0/nncase_kpu-2.11.0-py2.py3-none-win_amd64.whl" -OutFile "C:\Users\中文路径\Desktop\YOLOv8-yan\convert_ascii\nncase_kpu-2.11.0-py2.py3-none-win_amd64.whl"

作用:nncase KPU 加速库,用于 Kendryte KPU 模型编译

注意: - Python 版本要匹配 wheel 标签
            - nncase 和 nncase_kpu 版本要一致
            - .NET 用 7.x,且要和这版 nncase 能正常启动

我的信息:Python 版本是 3.9
                  pip 所在环境是 yolonew_env
                  我的环境是 Windows win-x64
                  环境里一开始没有装 nncase
                  我需要的是 K230 的 .kmodel,所以必须用 nncase + nncase_kpu 这套配套包

3.执行下载命令

(1)在终端执行:powershell -ExecutionPolicy Bypass -File .\convert_ascii\install_in_yolonew_env.ps1

作用:加载配置到虚拟环境

指令代码:

$ErrorActionPreference = "Stop"

$convertDir = $PSScriptRoot
$pythonExe = "C:\k230_env\Scripts\python.exe"
$pipExe = "C:\k230_env\Scripts\pip.exe"
$dotnetDir = Join-Path $convertDir "dotnet"
$dotnetInstallScript = Join-Path $convertDir "dotnet-install.ps1"
$nncaseWheel = Join-Path $convertDir "nncase-2.11.0-cp39-cp39-win_amd64.whl"
$nncaseKpuWheel = Join-Path $convertDir "nncase_kpu-2.11.0-py2.py3-none-win_amd64.whl"

if (-not (Test-Path $pythonExe)) {
    throw "Missing python: $pythonExe"
}

if (-not (Test-Path $nncaseWheel)) {
    throw "Missing file: $nncaseWheel"
}

if (-not (Test-Path $nncaseKpuWheel)) {
    throw "Missing file: $nncaseKpuWheel"
}

if (-not (Test-Path $dotnetInstallScript)) {
    throw "Missing file: $dotnetInstallScript"
}

if (-not (Test-Path $dotnetDir)) {
    New-Item -ItemType Directory -Path $dotnetDir -Force | Out-Null
}

$env:PYTHONDONTWRITEBYTECODE = "1"
& $pythonExe -m pip install --upgrade pip
& $pythonExe -m pip install --no-compile $nncaseWheel
& $pythonExe -m pip install --no-compile $nncaseKpuWheel
& powershell -ExecutionPolicy Bypass -File $dotnetInstallScript -Version 7.0.410 -InstallDir $dotnetDir

$env:DOTNET_ROOT = $dotnetDir
$env:PATH = "C:\k230_env\Scripts;$dotnetDir;$env:PATH"

Write-Host ""
Write-Host "Install finished."
Write-Host "Python: $pythonExe"
Write-Host "DOTNET_ROOT: $dotnetDir"

(2)在终端执行:powershell -ExecutionPolicy Bypass -File .\convert\run_best_onnx_to_kmodel.ps1

作用:onnx转kmodel(注意代码里的路径和模型尺寸等配置修改)

指令代码:

$ErrorActionPreference = "Stop"

$convertDir = $PSScriptRoot
$pythonExe = "C:\k230_env\Scripts\python.exe"
$dotnetDir = Join-Path $convertDir "dotnet"
$projectConverter = "C:\k230_proj\onnx_to_kmodel.py"
$onnxPath = "C:\k230_pipe\train\weights\best.onnx"
$outputPath = Join-Path $convertDir "best.kmodel"

if (-not (Test-Path $pythonExe)) {
    throw "Missing python: $pythonExe"
}

if (-not (Test-Path $projectConverter)) {
    throw "Missing converter script: $projectConverter"
}

$env:PYTHONDONTWRITEBYTECODE = "1"
$env:DOTNET_ROOT = $dotnetDir
$env:NNCASE_PLUGIN_PATH = "C:\k230_env\Lib\site-packages\nncase\modules"
$env:PATH = "C:\k230_env\Scripts;$dotnetDir;$env:PATH"

& $pythonExe $projectConverter `
    --onnx $onnxPath `
    --output $outputPath `
    --target k230 `
    --input-shape 1,3,320,320 `
    --no-quant

onnx_to_kmodel.py代码

import argparse
import os
import sys
from pathlib import Path

import nncase


def parse_csv_numbers(raw: str, cast):
    items = [item.strip() for item in raw.split(",") if item.strip()]
    if not items:
        raise argparse.ArgumentTypeError(f"Invalid value: {raw}")
    try:
        return [cast(item) for item in items]
    except ValueError as exc:
        raise argparse.ArgumentTypeError(str(exc)) from exc


def configure_plugin_path():
    site_packages = Path(nncase.__file__).resolve().parent.parent
    modules_dir = site_packages / "nncase" / "modules"
    if modules_dir.exists():
        os.environ["NNCASE_PLUGIN_PATH"] = str(modules_dir)
    return modules_dir


def create_compiler(args):
    options = nncase.CompileOptions()
    options.target = args.target
    options.input_file = str(args.onnx.resolve())
    options.preprocess = True
    options.input_type = args.input_type
    options.input_shape = args.input_shape
    options.input_range = [0.0, 1.0]
    options.mean = args.mean
    options.std = args.std
    options.input_layout = "NCHW"
    options.output_layout = "NCHW"
    options.dump_ir = False
    options.dump_asm = False
    return nncase.Compiler(options)


def load_ptq_dataset(dataset_dir: Path, input_shape):
    try:
        import cv2
        import numpy as np
    except ImportError as exc:
        raise RuntimeError("Quantized export needs opencv-python and numpy.") from exc

    exts = {".jpg", ".jpeg", ".png", ".bmp"}
    image_paths = [p for p in sorted(dataset_dir.iterdir()) if p.suffix.lower() in exts]
    if not image_paths:
        raise RuntimeError(f"No calibration images found in: {dataset_dir}")

    _, channels, height, width = input_shape
    samples = []
    for image_path in image_paths[:50]:
        image = cv2.imread(str(image_path))
        if image is None:
            continue
        image = cv2.resize(image, (width, height))
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        image = image.astype(np.float32) / 255.0
        image = image.transpose(2, 0, 1)
        image = np.expand_dims(image, axis=0)
        samples.append([image])

    if not samples:
        raise RuntimeError(f"Failed to load calibration images from: {dataset_dir}")
    return samples


def compile_kmodel(args):
    modules_dir = configure_plugin_path()
    print(f"NNCASE_PLUGIN_PATH={modules_dir}")

    if not nncase.check_target(args.target):
        raise RuntimeError(f"Unsupported target: {args.target}")

    compiler = create_compiler(args)
    import_options = nncase.ImportOptions()

    with open(args.onnx.resolve(), "rb") as f:
        compiler.import_onnx(f.read(), import_options)

    if not args.no_quant and args.dataset:
        ptq_options = nncase.PTQTensorOptions()
        ptq_options.quant_type = args.quant_type
        ptq_options.w_quant_type = args.quant_type
        ptq_options.samples_count = 5
        ptq_options.set_tensor_data(load_ptq_dataset(args.dataset.resolve(), args.input_shape))
        compiler.use_ptq(ptq_options)

    compiler.compile()

    output_path = args.output.resolve()
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with open(output_path, "wb") as f:
        compiler.gencode(f)


def main():
    default_onnx = Path(os.path.expanduser(r"~\Desktop\pipe_detection_v1\train\weights\best.onnx"))
    parser = argparse.ArgumentParser(
        description="Convert an ONNX model to Kendryte .kmodel using nncase/ncc."
    )
    parser.add_argument(
        "--onnx",
        type=Path,
        default=default_onnx,
        help="Path to the input ONNX file.",
    )
    parser.add_argument(
        "--output",
        type=Path,
        default=Path("best.kmodel"),
        help="Path to the output .kmodel file.",
    )
    parser.add_argument(
        "--target",
        default="k230",
        help="Kendryte target, for example: k230, k510, k210.",
    )
    parser.add_argument(
        "--input-shape",
        type=lambda x: parse_csv_numbers(x, int),
        default=[1, 3, 320, 320],
        help="Input tensor shape, for example: 1,3,320,320",
    )
    parser.add_argument(
        "--input-type",
        default="float32",
        help="Input tensor type used by nncase, for example: float32 or uint8.",
    )
    parser.add_argument(
        "--dataset",
        type=Path,
        help="Calibration dataset directory. Recommended for quantized kmodel export.",
    )
    parser.add_argument(
        "--mean",
        type=lambda x: parse_csv_numbers(x, float),
        default=[0.0, 0.0, 0.0],
        help="Normalization mean values, for example: 0,0,0",
    )
    parser.add_argument(
        "--std",
        type=lambda x: parse_csv_numbers(x, float),
        default=[255.0, 255.0, 255.0],
        help="Normalization std values, for example: 255,255,255",
    )
    parser.add_argument(
        "--quant-type",
        default="uint8",
        help="Quantization type when dataset is provided, for example: uint8.",
    )
    parser.add_argument(
        "--no-quant",
        action="store_true",
        help="Disable quantization. Useful when debugging compiler compatibility first.",
    )
    parser.add_argument(
        "--print-only",
        action="store_true",
        help="Print resolved paths and exit.",
    )
    args = parser.parse_args()

    if not args.onnx.exists():
        print(f"ONNX file not found: {args.onnx}", file=sys.stderr)
        sys.exit(1)

    if args.dataset and not args.dataset.exists():
        print(f"Dataset directory not found: {args.dataset}", file=sys.stderr)
        sys.exit(1)

    print(f"ONNX: {args.onnx.resolve()}")
    print(f"Output: {args.output.resolve()}")
    print(f"Target: {args.target}")
    print(f"Input shape: {args.input_shape}")
    print(f"Input type: {args.input_type}")
    print(f"Quantized: {not args.no_quant and bool(args.dataset)}")

    if args.print_only:
        return

    try:
        compile_kmodel(args)
    except Exception as exc:
        print(
            f"\nCompilation failed: {exc}\n"
            "Common reasons:\n"
            "1. target does not match your chip\n"
            "2. ONNX input shape or unsupported operators need adjustment\n"
            "3. K230 plugin path or runtime environment is incomplete",
            file=sys.stderr,
        )
        sys.exit(1)

    print(f"\nKModel export finished: {args.output.resolve()}")


if __name__ == "__main__":
    main()

注意:各个代码里都附带了文件路径,.pt文件地址等等,必须确定该地址正确

更多推荐