RK3588部署yolov8过程记录

一、模型训练

这一步和大多数教程写的一样,就不赘述了。获取到’.pt’格式的权重即可。

二、模型转换pt—>onnx

1.下载转换工具库

转换工具库链接:https://github.com/airockchip/ultralytics_yolov8
下载后,进入配置好的环境下进行转换。

2.配置修改

修改./ultralytics/cfg/default.yaml中的模型路径
在这里插入图片描述
回到项目目录下,输入:

export PYTHONPATH=./
python ./ultralytics/engine/exporter.py

如图
在这里插入图片描述

三、模型转换onnx—>rknn

1.配置rknn-toolkit2环境

这一部分要配置rknn-toolkit2环境,下载rknn-toolkit2:https://github.com/airockchip/rknn-toolkit2

版本需要留意一下,需要与后面的rknn_model_zoo版本一致,这里我使用的是2.3.2的版本
先创建一个python3.9的环境,单独给rknn-toolkit2使用。

conda create -n rknn-toolkit2 python=3.9   #创建虚拟环境
conda activate rknn-toolkit2  #激活该环境
cd rknn-toolkit2/rknn-toolkit2/packages/x86_64/ #进入该目录下
pip install -r requirements_cp39-2.3.2.txt -i https://pypi.tuna.tsinghua.edu.cn/simple  #安装依赖
pip install rknn_toolkit2-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

刚才的虚拟环境下,调用python验证

from rknn.api import RKNN
2.量化配置及转换

需要选取一些图像,放置于dataset文件夹,然后新建dataset.txt文件,改txt文件中包含dataset文件夹下图像的绝对路径。
在这里插入图片描述

接下来利用rknn_model_zoo/examples/yolov8/python中的代码进行转换
在convert.py中修改dataset_path(量化图像路径)
在这里插入图片描述
在yolov8.py中修改:
在这里插入图片描述

在配置好rknn-toolkit2的虚拟环境下输入指令进行转换

python convert.py /home/chenjun0310/SmokeAndFire_Yolov8/ultralytics_yolov8/last.onnx rk3588

在这里插入图片描述
在model文件夹下会生成对应名字的rknn格式的文件

3.精度分析

模型经过转换后,会存在一些精度损失,需要使用rknn的模拟器进行精度分析,获取优化方向。代码如下:

#!/usr/bin/env python3
"""
RKNN 模型综合精度分析脚本
1. 使用 RKNN accuracy_analysis API(模拟器模式)
2. ONNX vs RKNN 输出对比
3. 检测结果对比
"""

import os
import sys
import numpy as np
import cv2
import json
from datetime import datetime

# 添加路径
sys.path.insert(0, '/home/chenjun0310/SmokeAndFire_Yolov11/rknn_model_zoo')

from rknn.api import RKNN
import onnxruntime as ort

# ============ 配置 ============
ONNX_MODEL = '/home/chenjun0310/SmokeAndFire_Yolov8/TrainCode/runs/train/smoke_fire_default_1/weights/best.onnx'
RKNN_MODEL = '/home/chenjun0310/SmokeAndFire_Yolov11/rknn_model_zoo/examples/yolov8/model/Firesmokeyolov8.rknn'
VAL_IMG_FOLDER = '/home/chenjun0310/fireSmoke/fireSmoke_org/images/val'
QUANT_DATASET = '/home/chenjun0310/SmokeAndFire_Yolov11/rknn-toolkit2/MyCode/dataset.txt'
OUTPUT_DIR = '/home/chenjun0310/rknn_analysis_results'
TARGET_PLATFORM = 'rk3588'

# 转换参数(与 convert.py 一致)
MEAN_VALUES = [[0, 0, 0]]
STD_VALUES = [[255, 255, 255]]

def get_test_images(folder, max_count=20):
    """获取测试图像列表"""
    valid_exts = ['.jpg', '.jpeg', '.png', '.bmp']
    imgs = []
    for f in sorted(os.listdir(folder)):
        if any(f.lower().endswith(ext) for ext in valid_exts):
            imgs.append(os.path.join(folder, f))
    return imgs[:max_count]

def preprocess_image(img_path, input_size=640):
    """YOLOv8 预处理 - 返回归一化后的 NCHW 张量"""
    img = cv2.imread(img_path)
    if img is None:
        return None, None
    
    # Resize to 640x640
    img_resized = cv2.resize(img, (input_size, input_size))
    
    # BGR to RGB, normalize
    img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)
    img_float = img_rgb.astype(np.float32) / 255.0
    
    # HWC to CHW, add batch dim
    img_chw = np.transpose(img_float, (2, 0, 1))
    input_tensor = np.expand_dims(img_chw, axis=0)
    
    return input_tensor, img_rgb

def cosine_similarity(a, b):
    """计算余弦相似度"""
    a_flat = a.flatten().astype(np.float64)
    b_flat = b.flatten().astype(np.float64)
    dot = np.dot(a_flat, b_flat)
    norm_a = np.linalg.norm(a_flat)
    norm_b = np.linalg.norm(b_flat)
    if norm_a < 1e-8 or norm_b < 1e-8:
        return 1.0 if np.allclose(a_flat, b_flat) else 0.0
    return dot / (norm_a * norm_b)

def analyze_tensor_diff(onnx_out, rknn_out, name):
    """分析两个张量的差异"""
    onnx_arr = np.asarray(onnx_out).astype(np.float32)
    rknn_arr = np.asarray(rknn_out).astype(np.float32)
    
    diff = onnx_arr - rknn_arr
    
    result = {
        'name': name,
        'shape': list(onnx_arr.shape),
        'onnx_min': float(np.min(onnx_arr)),
        'onnx_max': float(np.max(onnx_arr)),
        'onnx_mean': float(np.mean(onnx_arr)),
        'onnx_std': float(np.std(onnx_arr)),
        'rknn_min': float(np.min(rknn_arr)),
        'rknn_max': float(np.max(rknn_arr)),
        'rknn_mean': float(np.mean(rknn_arr)),
        'rknn_std': float(np.std(rknn_arr)),
        'mae': float(np.mean(np.abs(diff))),
        'mse': float(np.mean(diff ** 2)),
        'rmse': float(np.sqrt(np.mean(diff ** 2))),
        'max_abs_diff': float(np.max(np.abs(diff))),
        'cosine_sim': float(cosine_similarity(onnx_arr, rknn_arr)),
    }
    
    with np.errstate(divide='ignore', invalid='ignore'):
        rel_err = np.abs(diff) / (np.abs(onnx_arr) + 1e-8)
        valid_rel = rel_err[np.isfinite(rel_err)]
        result['mean_rel_err'] = float(np.mean(valid_rel)) if len(valid_rel) > 0 else 0.0
    
    return result

def run_accuracy_analysis_api(onnx_path, output_dir):
    """使用 RKNN 官方 accuracy_analysis API"""
    print("\n" + "=" * 80)
    print("第一部分:RKNN 官方 accuracy_analysis API 分析")
    print("=" * 80)
    
    rknn = RKNN(verbose=True)
    
    print("\n[1] 配置模型参数...")
    rknn.config(
        mean_values=MEAN_VALUES,
        std_values=STD_VALUES,
        target_platform=TARGET_PLATFORM
    )
    
    print("\n[2] 加载 ONNX 模型...")
    ret = rknn.load_onnx(model=onnx_path)
    if ret != 0:
        print(f"ERROR: 加载 ONNX 失败 (ret={ret})")
        rknn.release()
        return False
    
    print("\n[3] 构建量化模型...")
    ret = rknn.build(do_quantization=True, dataset=QUANT_DATASET)
    if ret != 0:
        print(f"ERROR: 构建模型失败 (ret={ret})")
        rknn.release()
        return False
    
    print("\n[4] 初始化运行时(模拟器模式)...")
    ret = rknn.init_runtime()
    if ret != 0:
        print(f"ERROR: 初始化运行时失败 (ret={ret})")
        rknn.release()
        return False
    
    print("\n[5] 准备精度分析数据集...")
    test_images = get_test_images(VAL_IMG_FOLDER, max_count=10)
    dataset_file = os.path.join(output_dir, 'test_dataset.txt')
    with open(dataset_file, 'w') as f:
        for img_path in test_images:
            f.write(img_path + '\n')
    print(f"使用 {len(test_images)} 张测试图像")
    
    print("\n[6] 执行 accuracy_analysis()...")
    accuracy_output_dir = os.path.join(output_dir, 'accuracy_analysis_output')
    os.makedirs(accuracy_output_dir, exist_ok=True)
    
    try:
        ret = rknn.accuracy_analysis(
            inputs=dataset_file,
            output_dir=accuracy_output_dir
        )
        if ret == 0:
            print(f"\n精度分析完成!结果保存在: {accuracy_output_dir}")
            for root, dirs, files in os.walk(accuracy_output_dir):
                for f in files:
                    print(f"  - {os.path.relpath(os.path.join(root, f), accuracy_output_dir)}")
            rknn.release()
            return True
        else:
            print(f"WARNING: accuracy_analysis 返回非零值: {ret}")
    except Exception as e:
        print(f"WARNING: accuracy_analysis 异常: {e}")
        print("将使用手动对比方法...")
    
    rknn.release()
    return False

def run_manual_comparison(onnx_path, rknn_path, output_dir):
    """手动对比 ONNX 和 RKNN 输出 - 从ONNX重新构建RKNN用于模拟器"""
    print("\n" + "=" * 80)
    print("第二部分:ONNX vs RKNN 逐层输出对比")
    print("=" * 80)
    
    print("\n[1] 加载 ONNX 模型...")
    onnx_session = ort.InferenceSession(onnx_path, providers=['CPUExecutionProvider'])
    onnx_inputs = onnx_session.get_inputs()
    onnx_outputs = onnx_session.get_outputs()
    print(f"ONNX 输入: {[i.name for i in onnx_inputs]}, shape: {onnx_inputs[0].shape}")
    print(f"ONNX 输出: {len(onnx_outputs)} 个")
    for o in onnx_outputs:
        print(f"  - {o.name}: {o.shape}")
    
    print("\n[2] 从ONNX构建RKNN模型(用于模拟器推理)...")
    rknn = RKNN(verbose=False)
    
    # 配置与转换时相同的参数
    rknn.config(
        mean_values=MEAN_VALUES,
        std_values=STD_VALUES,
        target_platform=TARGET_PLATFORM
    )
    
    ret = rknn.load_onnx(model=onnx_path)
    if ret != 0:
        print(f"ERROR: 加载 ONNX 失败 (ret={ret})")
        return None
    
    print("  构建量化模型...")
    ret = rknn.build(do_quantization=True, dataset=QUANT_DATASET)
    if ret != 0:
        print(f"ERROR: 构建模型失败 (ret={ret})")
        return None
    
    ret = rknn.init_runtime()  # 模拟器模式
    if ret != 0:
        print(f"ERROR: 初始化运行时失败 (ret={ret})")
        return None
    print("RKNN 初始化成功(模拟器模式)")
    
    test_images = get_test_images(VAL_IMG_FOLDER, max_count=10)
    print(f"\n[3] 使用 {len(test_images)} 张图像进行对比测试")
    
    all_results = []
    
    for idx, img_path in enumerate(test_images):
        print(f"\n  [{idx+1}/{len(test_images)}] {os.path.basename(img_path)}")
        
        input_tensor, img_rgb = preprocess_image(img_path)
        if input_tensor is None:
            continue
        
        # ONNX 推理 (NCHW, float32, 0-1)
        onnx_results = onnx_session.run(
            [o.name for o in onnx_outputs],
            {onnx_inputs[0].name: input_tensor}
        )
        
        # RKNN 推理 (NHWC, uint8, 0-255 因为配置了 mean=[0,0,0], std=[255,255,255])
        rknn_input = np.expand_dims(img_rgb, axis=0).astype(np.uint8)
        rknn_results = rknn.inference(inputs=[rknn_input])
        
        img_result = {'image': os.path.basename(img_path), 'outputs': []}
        
        for out_idx in range(min(len(onnx_results), len(rknn_results))):
            output_name = onnx_outputs[out_idx].name if out_idx < len(onnx_outputs) else f"output_{out_idx}"
            diff_result = analyze_tensor_diff(onnx_results[out_idx], rknn_results[out_idx], output_name)
            img_result['outputs'].append(diff_result)
            
            print(f"    输出 {out_idx} ({output_name}): MAE={diff_result['mae']:.6f}, "
                  f"Cosine={diff_result['cosine_sim']:.4f}, "
                  f"MaxDiff={diff_result['max_abs_diff']:.6f}")
        
        all_results.append(img_result)
    
    rknn.release()
    return all_results

def generate_report(results, output_dir):
    """生成分析报告"""
    print("\n" + "=" * 80)
    print("第三部分:生成分析报告")
    print("=" * 80)
    
    if not results:
        print("WARNING: 没有分析结果")
        return
    
    num_outputs = len(results[0]['outputs']) if results else 0
    output_stats = {i: {'mae': [], 'cosine': [], 'max_diff': [], 'rel_err': []} for i in range(num_outputs)}
    
    for img_result in results:
        for i, out in enumerate(img_result['outputs']):
            output_stats[i]['mae'].append(out['mae'])
            output_stats[i]['cosine'].append(out['cosine_sim'])
            output_stats[i]['max_diff'].append(out['max_abs_diff'])
            output_stats[i]['rel_err'].append(out['mean_rel_err'])
    
    print("\n【汇总统计 - 所有图像平均】")
    print(f"{'输出层':<20} {'MAE':<12} {'余弦相似度':<12} {'最大绝对差':<12} {'平均相对误差':<12}")
    print("-" * 68)
    
    # YOLOv8 9输出: 3尺度 × (box + class + score_sum)
    output_types = ['box', 'class', 'score'] * 3
    scale_names = ['80x80', '40x40', '20x20']
    
    problem_outputs = []
    
    for i in range(num_outputs):
        mae_avg = np.mean(output_stats[i]['mae'])
        cos_avg = np.mean(output_stats[i]['cosine'])
        max_diff_avg = np.mean(output_stats[i]['max_diff'])
        rel_err_avg = np.mean(output_stats[i]['rel_err'])
        
        scale_idx = i // 3
        type_idx = i % 3
        output_type = output_types[i] if i < len(output_types) else 'unknown'
        scale = scale_names[scale_idx] if scale_idx < len(scale_names) else '?'
        
        name = f"out{i}({scale}/{output_type})"
        
        flag = ""
        if cos_avg < 0.95:
            flag = " ⚠️"
            problem_outputs.append({'index': i, 'scale': scale, 'type': output_type, 
                                   'cosine': cos_avg, 'mae': mae_avg})
        
        print(f"{name:<20} {mae_avg:<12.6f} {cos_avg:<12.4f} {max_diff_avg:<12.6f} {rel_err_avg:<12.6f}{flag}")
    
    print("\n【量化误差分析】")
    if problem_outputs:
        print("发现以下输出层量化误差较大:")
        for p in problem_outputs:
            print(f"  - 输出 {p['index']} ({p['scale']}/{p['type']}): 余弦相似度={p['cosine']:.4f}, MAE={p['mae']:.6f}")
        print("\n【改进建议】")
        print("1. 增加量化校准数据集数量(当前80张,建议200+张)")
        print("2. 考虑使用混合精度量化,对误差大的层保持 FP16")
        print("3. 检查量化数据集是否包含足够多样的烟火样本")
        print("4. 如果 class/score 层误差大,可能影响分类置信度,考虑调整后处理阈值")
    else:
        print("所有输出层量化误差在可接受范围内(余弦相似度 >= 0.95)")
    
    # 保存 JSON 报告
    report_path = os.path.join(output_dir, 'accuracy_comparison_report.json')
    report_data = {
        'timestamp': datetime.now().isoformat(),
        'onnx_model': ONNX_MODEL,
        'rknn_model': RKNN_MODEL,
        'conversion_params': {
            'quantization': 'INT8',
            'mean_values': MEAN_VALUES,
            'std_values': STD_VALUES,
            'target_platform': TARGET_PLATFORM,
            'quant_dataset_size': 80
        },
        'num_test_images': len(results),
        'summary': {},
        'detailed_results': results
    }
    
    for i in range(num_outputs):
        report_data['summary'][f'output_{i}'] = {
            'avg_mae': float(np.mean(output_stats[i]['mae'])),
            'avg_cosine': float(np.mean(output_stats[i]['cosine'])),
            'avg_max_diff': float(np.mean(output_stats[i]['max_diff'])),
            'avg_rel_err': float(np.mean(output_stats[i]['rel_err']))
        }
    
    with open(report_path, 'w') as f:
        json.dump(report_data, f, indent=2, ensure_ascii=False)
    print(f"\n详细报告已保存: {report_path}")

def main():
    print("=" * 80)
    print("RKNN 模型综合精度分析")
    print("=" * 80)
    print(f"\nONNX 模型: {ONNX_MODEL}")
    print(f"RKNN 模型: {RKNN_MODEL}")
    print(f"验证集: {VAL_IMG_FOLDER}")
    print(f"输出目录: {OUTPUT_DIR}")
    
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    
    # 第一部分:尝试使用官方 API
    api_success = run_accuracy_analysis_api(ONNX_MODEL, OUTPUT_DIR)
    
    # 第二部分:手动对比
    results = run_manual_comparison(ONNX_MODEL, RKNN_MODEL, OUTPUT_DIR)
    
    # 第三部分:生成报告
    if results:
        generate_report(results, OUTPUT_DIR)
    
    print("\n" + "=" * 80)
    print("分析完成!")
    print(f"结果保存在: {OUTPUT_DIR}")
    print("=" * 80)

if __name__ == '__main__':
    main()

运行完会输出各个输出层的余弦相似度。

4.混合量化

生成量化配置文件
在rknn_model_zoo/examples/yolov8/python中加入如下代码:
convert_hybrid_quant_step1.py,生成best.quantization.cfg 文件

#!/usr/bin/env python3
"""
#!/usr/bin/env python3
"""
YOLOv8 RKNN Hybrid Quantization - Step 1
生成量化配置文件用于分析各层对量化的敏感度

此脚本用于:
1. 加载ONNX模型
2. 执行 hybrid_quantization_step1() 自动分析各层敏感度
3. 生成 .quantization.cfg 配置文件供用户编辑
"""
import sys
import os
from rknn.api import RKNN

DATASET_PATH = '/home/chenjun0310/SmokeAndFire_Yolov11/rknn-toolkit2/MyCode/dataset.txt'

def main():
    if len(sys.argv) < 3:
        print("用法: python3 {} <onnx_model_path> <platform> [output_cfg_dir]".format(sys.argv[0]))
        print("")
        print("参数说明:")
        print("  onnx_model_path : ONNX 模型路径")
        print("  platform        : 目标平台 (rk3588, rk3566, rk3568, rk3576, rk3562, rv1126b 等)")
        print("  output_cfg_dir  : 输出配置文件目录 (默认: 当前目录)")
        print("")
        print("示例:")
        print("  python3 {} /path/to/best.onnx rk3588".format(sys.argv[0]))
        sys.exit(1)

    model_path = sys.argv[1]
    platform = sys.argv[2]
    cfg_output_dir = sys.argv[3] if len(sys.argv) > 3 else '.'

    if not os.path.exists(model_path):
        print(f"错误: 模型文件不存在 - {model_path}")
        sys.exit(1)

    print("=" * 80)
    print("RKNN YOLOv8 混合精度量化 - Step 1: 生成配置文件")
    print("=" * 80)
    print(f"模型路径: {model_path}")
    print(f"目标平台: {platform}")
    print(f"数据集: {DATASET_PATH}")
    print(f"输出目录: {cfg_output_dir}")
    print("")

    # 创建 RKNN 对象
    print("--> 创建 RKNN 对象")
    rknn = RKNN(verbose=True)
    print("done\n")

    # 配置模型
    print("--> 配置模型")
    rknn.config(
        mean_values=[[0, 0, 0]], 
        std_values=[[255, 255, 255]], 
        target_platform=platform
    )
    print("done\n")

    # 加载模型
    print("--> 加载 ONNX 模型")
    ret = rknn.load_onnx(model=model_path)
    if ret != 0:
        print('加载模型失败!')
        rknn.release()
        sys.exit(ret)
    print("done\n")

    # 执行 hybrid_quantization_step1
    print("--> 执行 hybrid_quantization_step1 (分析各层敏感度)")
    print("    这可能需要几分钟时间...\\n")
    ret = rknn.hybrid_quantization_step1(dataset=DATASET_PATH, proposal=False)
    if ret != 0:
        print('hybrid_quantization_step1 失败!')
        rknn.release()
        sys.exit(ret)
    print("done\n")

    # 获取模型基名
    model_basename = os.path.basename(model_path)
    cfg_filename = os.path.splitext(model_basename)[0] + '.quantization.cfg'
    cfg_path = os.path.join(cfg_output_dir, cfg_filename)

    # 输出提示信息
    print("=" * 80)
    print("混合精度量化配置指南")
    print("=" * 80)
    print("")
    print("配置文件已生成: " + cfg_path)
    print("")
    print("配置文件说明:")
    print("  - 文件格式为 YAML")
    print("  - 'custom_quantize_layers' 部分列出建议需要特殊处理的层")
    print("")
    print("配置步骤:")
    print("  1. 编辑生成的配置文件")
    print("  2. 在 'custom_quantize_layers' 中添加需要FP16精度的输出层")
    print("")
    print("YOLOv8 模型推荐配置 (用于小目标检测精度优化):")
    print("  custom_quantize_layers:")
    print("      output7: float16          # 80x80 scale - class scores (小目标分类)")
    print("      output8: float16          # 80x80 scale - score_sum")
    print("      output4: float16          # 40x40 scale - class scores (中尺度分类)")
    print("      output5: float16          # 40x40 scale - score_sum")
    print("")
    print("支持的精度类型: int4, int8, int16, float16")
    print("")
    print("建议策略:")
    print("  - 保持 box regression (output0, output3, output6) 为 int8 (节省空间)")
    print("  - 将分类层 (output1, output4, output7) 设为 float16 (保持精度)")
    print("  - score_sum (output2, output5, output8) 根据需要设为 float16")
    print("")
    print("完成配置后,执行 Step 2:")
    print(f"  python3 convert_hybrid_quant_step2.py {model_path} {platform} {cfg_path}")
    print("")
    print("=" * 80)

    rknn.release()
    print("Step 1 完成!\\n")

if __name__ == '__main__':
    main()


输入指令:

python3 convert_hybrid_quant_step1.py <ONNX模型路径> <目标平台> <输出目录>
python3 convert_hybrid_quant_step1.py \
    /home/chenjun0310/SmokeAndFire_Yolov8/TrainCode/runs/train/smoke_fire_default_1/weights/best.onnx \
    rk3588 \
    .

会生成
这会在当前目录生成 3 个文件:
best.quantization.cfg — 量化配置(需要修改的文件)
best.model — 中间模型文件
best.data — 量化参数数据

打开生成的 best.quantization.cfg,在文件中找到 custom_quantize_layers 部分,添加需要设为 FP16 的层:
如:

custom_quantize_layers:
    /model.22/cv3.0/cv3.0.2/Conv_output_0: float16      # 80x80 分类层
    /model.22/ReduceSum_output_0: float16                # 80x80 score_sum
    /model.22/cv3.1/cv3.1.2/Conv_output_0: float16      # 40x40 分类层
    /model.22/ReduceSum_1_output_0: float16              # 40x40 score_sum
    /model.22/cv3.2/cv3.2.2/Conv_output_0: float16      # 20x20 分类层
    /model.22/ReduceSum_2_output_0: float16              # 20x20 score_sum

再新建convert_hybrid_quant_step2.py文件

#!/usr/bin/env python3
"""
YOLOv8 RKNN Hybrid Quantization - Step 2
根据配置文件执行混合精度量化并生成最终RKNN模型

此脚本用于:
1. 读取 Step 1 生成的量化配置文件
2. 执行 hybrid_quantization_step2() 进行混合精度量化
3. 导出最终的 RKNN 模型
4. 进行精度分析验证
"""
import sys
import os
from rknn.api import RKNN

DEFAULT_RKNN_PATH = '../model/Firesmokeyolov8_hybrid.rknn'

def main():
    if len(sys.argv) < 3:
        print("用法: python3 {} <onnx_model_path> <platform> <quantization_cfg> [output_rknn_path]".format(sys.argv[0]))
        print("")
        print("参数说明:")
        print("  onnx_model_path     : ONNX 模型路径")
        print("  platform            : 目标平台 (rk3588, rk3566, rk3568, rk3576, rk3562, rv1126b 等)")
        print("  quantization_cfg    : 量化配置文件路径 (由 Step 1 生成的 .quantization.cfg)")
        print("  output_rknn_path    : 输出 RKNN 模型路径 (默认: " + DEFAULT_RKNN_PATH + ")")
        print("")
        print("示例:")
        print("  python3 {} /path/to/best.onnx rk3588 /path/to/best.quantization.cfg".format(sys.argv[0]))
        sys.exit(1)

    model_path = sys.argv[1]
    platform = sys.argv[2]
    cfg_path = sys.argv[3]
    output_path = sys.argv[4] if len(sys.argv) > 4 else DEFAULT_RKNN_PATH

    # 参数校验
    if not os.path.exists(model_path):
        print(f"错误: ONNX 模型文件不存在 - {model_path}")
        sys.exit(1)

    if not os.path.exists(cfg_path):
        print(f"错误: 配置文件不存在 - {cfg_path}")
        sys.exit(1)

    print("=" * 80)
    print("RKNN YOLOv8 混合精度量化 - Step 2: 执行混合精度量化")
    print("=" * 80)
    print(f"ONNX 模型: {model_path}")
    print(f"目标平台: {platform}")
    print(f"量化配置: {cfg_path}")
    print(f"输出RKNN: {output_path}")
    print("")

    # 查找 Step 1 生成的 .model 和 .data 文件
    # 注意: hybrid_quantization_step1() 会把文件输出到当前工作目录,而不是 ONNX 所在目录
    model_basename = os.path.splitext(os.path.basename(model_path))[0]
    
    # 候选路径列表 (按优先级排序)
    search_paths = [
        os.getcwd(),                          # 1. 当前工作目录 (Step 1 默认输出位置)
        os.path.dirname(model_path),          # 2. ONNX 文件所在目录
        os.path.dirname(os.path.abspath(__file__)),  # 3. 脚本所在目录
    ]
    
    model_file = None
    data_file = None
    
    # 查找 .model 文件
    for search_path in search_paths:
        candidate = os.path.join(search_path, model_basename + '.model')
        if os.path.exists(candidate):
            model_file = candidate
            break
    
    # 查找 .data 文件
    for search_path in search_paths:
        candidate = os.path.join(search_path, model_basename + '.data')
        if os.path.exists(candidate):
            data_file = candidate
            break
    
    if model_file is None:
        print(f"错误: Step 1 生成的 .model 文件不存在")
        print(f"已查找的位置:")
        for path in search_paths:
            print(f"  - {os.path.join(path, model_basename + '.model')}")
        print("")
        print("请先执行 Step 1 脚本生成必要的中间文件,或检查文件是否在上述位置之一")
        sys.exit(1)
    
    if data_file is None:
        print(f"错误: Step 1 生成的 .data 文件不存在")
        print(f"已查找的位置:")
        for path in search_paths:
            print(f"  - {os.path.join(path, model_basename + '.data')}")
        print("")
        print("请先执行 Step 1 脚本生成必要的中间文件,或检查文件是否在上述位置之一")
        sys.exit(1)
    
    print(f".model 文件: {model_file}")
    print(f".data 文件: {data_file}")
    print("")

    # 创建 RKNN 对象
    print("--> 创建 RKNN 对象")
    rknn = RKNN(verbose=True)
    print("done\n")

    # 执行 hybrid_quantization_step2
    print("--> 执行 hybrid_quantization_step2")
    print("    这可能需要几分钟时间...\\n")
    ret = rknn.hybrid_quantization_step2(
        model_input=model_file,
        data_input=data_file,
        model_quantization_cfg=cfg_path
    )
    if ret != 0:
        print('hybrid_quantization_step2 失败!')
        rknn.release()
        sys.exit(ret)
    print("done\n")

    # 创建输出目录
    output_dir = os.path.dirname(output_path)
    if output_dir and not os.path.exists(output_dir):
        os.makedirs(output_dir, exist_ok=True)

    # 导出 RKNN 模型
    print("--> 导出 RKNN 模型")
    ret = rknn.export_rknn(output_path)
    if ret != 0:
        print('导出 RKNN 模型失败!')
        rknn.release()
        sys.exit(ret)
    print(f"done -> {output_path}\n")

    # 精度分析 (可选)
    print("--> 执行精度分析")
    print("    注: 此步骤仅在有真实验证数据时有用")
    try:
        # 这里需要提供实际的验证图像
        # ret = rknn.accuracy_analysis(inputs=['./test_image.jpg'], output_dir=None)
        # 如果没有验证图像,跳过此步骤
        print("跳过精度分析 (需要提供验证数据)")
    except Exception as e:
        print(f"精度分析异常 (可忽略): {e}")
    print("done\n")

    # 初始化运行时 (可选)
    print("--> 初始化运行时环境")
    try:
        ret = rknn.init_runtime()
        if ret == 0:
            print("done\n")
        else:
            print("初始化失败 (可能未连接 NPU 设备)\n")
    except Exception as e:
        print(f"初始化异常 (可忽略): {e}\n")

    rknn.release()

    print("=" * 80)
    print("混合精度量化完成!")
    print("=" * 80)
    print("")
    print("生成的 RKNN 模型: " + output_path)
    print("")
    print("后续步骤:")
    print("  1. 将 RKNN 模型部署到 RK3588 等设备上")
    print("  2. 运行推理测试,评估模型精度")
    print("  3. 如果精度不满足,可调整配置文件中的量化精度后重新执行 Step 2")
    print("")

if __name__ == '__main__':
    main()


开始混合量化

python3 convert_hybrid_quant_step2.py <ONNX模型路径> <目标平台> <配置文件> <输出RKNN路径>

python3 convert_hybrid_quant_step2.py \
    /home/chenjun0310/SmokeAndFire_Yolov8/TrainCode/runs/train/smoke_fire_default_1/weights/best.onnx \
    rk3588 \
    best.quantization.cfg \
    ../model/Firesmokeyolov8_hybrid.rknn

四、PC端编译

rknn_model_zoo的pc端,确认Pc端和开发板端的gcc版本是否匹配,pc端编译,编译后拷贝到开发板进行测试。

1.确认gcc版本

我遇到的情况是PC 使用的是 Ubuntu 22.04 自带的 GCC 11.4.0。
这个版本的编译器默认生成的二进制文件需要 GLIBC 2.34+,但是 RK3588 开发板(通常是 Ubuntu 18.04 或 20.04 基础)只有 GLIBC 2.27 或 2.31。

# 1. 创建一个目录存放工具链
mkdir -p ~/toolchains
cd ~/toolchains

# 2. 下载 Linaro GCC 7.5 (2019.12 版本),这个版本非常稳定且兼容老系统
# 如果 wget 速度慢,可以尝试用 curl 或者找国内镜像
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 3. 解压
tar -xf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 4. 确认解压后的文件夹名称 (通常是 gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu)
ls
2.rknn_model_zoo/examples/yolov8/cpp代码修改

postprocess.h修改
文件路径:rknn_model_zoo/examples/yolov8/cpp/postprocess.h

// 将OBJ_CLASS_NUM从80改为2
#define OBJ_CLASS_NUM 2

postprocess.cc修改

#define LABEL_NAME_TXT_PATH "./model/labels_list.txt"

在这里插入图片描述

3.编译

终端进入rknn_model_zoo

指定编译器

export GCC_COMPILER=/home/chenjun0310/toolchains/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu

进行编译

rm -rf build/build_rknn_yolov8_demo_rk3588_linux_aarch64_Release

编译结束后,会生成rknn_model_zoo/install文件夹

五、开发板部署

1.复制文件

将install文件夹,复制到开发板,然后准备好测试数据。

2.测试

在install/rk3588_linux_aarch64/rknn_yolov8_demo下依次输入

chmod +x rknn_yolov8_demo
chmod +x rknn_yolov8_demo_zero_copy
export LD_LIBRARY_PATH=./lib
./rknn_yolov8_demo model/yolov8.rknn model/69.jpg
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐