CanMV K230使用经验分享

CanMV K230使用经验-初稿

CanMV K230开发板简介

K230宣传了很久,K230的开发板目前还处于缺货的状态,价格在250左右,CanMV K230 在官网已经更新了资料,包括镜像和教程。
官网地址:https://www.canaan-creative.com/
在官网的开发者社区页面可以看到镜像、开发工具、在线文档等资料。

在这里插入图片描述

开发板图

其余信息可以查阅官网信息

CanMV K230开发板使用案例

CanMV-IDE可以通过python变成开发,简化了开发过程,但还是涉及kmodel转换
主要流程为开发模型,模型转换及测试,模型部署.

CanMV K230开发板Micropython开发

模型转换

import tensorflow as tf 
import numpy as np
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
#拟合一个一次函数 Y=2X-1
l0 = Dense(units=1, input_shape=[1])
model = Sequential([l0])
model.compile(optimizer='sgd', loss='mean_squared_error')
xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)
model.fit(xs, ys, epochs=500)

print(model.predict([10.0])) #测试结果
print("Here is what I learned: {}".format(l0.get_weights()))

在训练完模型后需要转换为tflite或onnx格式文件,推荐转换为onnx文件,tflite文件在输入维度少时可能会存在动态形状难以转换为kmodel模型。

import tensorflow as tf
import os
import onnx
#需要先使用model.save方法保存模型
model.save('model')
#调用tf2onnx将上一步保存的模型导出为ONNX
os.system("python3 -m tf2onnx.convert --saved-model model --output test1.onnx --opset 11")

检测是否转换成功

import onnx

onnx_model = onnx.load("./test1.onnx")
check = onnx.checker.check_model(onnx_model)
print('Check: ', check) # 返回 Check:  None 为成功

还需要调整输入维度,因为转换为onnx模型后存在输入输出维度异常
在netron上打开onnx模型,如下图所示

输入输出tensor异常
通过下面代码修正

import onnx
onnx_model = onnx.load("./test1.onnx")
onnx_model.graph.input[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx_model.graph.output[0].type.tensor_type.shape.dim[0].dim_value = 1
onnx.save(onnx_model, './test1_dim.onnx')

修正结果如图
输入输出tensor修正准换onnx为kmodel模型,便于部署在k230开发板上,转换代码如下:
配置环境变量

import os
import sys
import subprocess

result = subprocess.run(["pip", "show", "nncase"], capture_output=True)

split_flag = "\n"
if sys.platform == "win32":
    split_flag = "\r\n"
    
location_s = [i for i in result.stdout.decode().split(split_flag) if i.startswith("Location:")]
location = location_s[0].split(": ")[1]

if "PATH" in os.environ:
    os.environ["PATH"] += os.pathsep + location
else:
    os.environ["PATH"] = location

定义转换参数

import nncase
import numpy as np
from nncase_base_func import *

def compile_kmodel(model_path, dump_path, calib_data):
    """
    Set compile options and ptq options.
    Compile kmodel.
    Dump the compile-time result to 'compile_options.dump_dir'
    """
    print("\n----------   compile    ----------")
    print("Simplify...")
    model_file = model_simplify(model_path)

    print("Set options...")
    # import_options
    import_options = nncase.ImportOptions()

    ############################################
    # The code below, you need to modify to fit your model.
    # You can find more details about these options in docs/USAGE_v2.md.
    ############################################
    # compile_options
    compile_options = nncase.CompileOptions()
    compile_options.target = "k230" #"cpu"
    compile_options.dump_ir = False  # if False, will not dump the compile-time result.
    compile_options.dump_asm = True
    compile_options.dump_dir = dump_path
    compile_options.input_file = ""
    
    # preprocess args   不采用预处理过程,应用与原始数据与训练模型输入之间的差异
    compile_options.preprocess = False#True
    if compile_options.preprocess:
        compile_options.input_type = "float32" # "uint8" "float32"
        compile_options.input_shape = [1,1]
        compile_options.input_range = [-1,4]
        compile_options.input_layout = "" # "NHWC"
        compile_options.swapRB = False
        compile_options.mean = [1.5]
        compile_options.std = [1.7]
        compile_options.letterbox_value = 0
        compile_options.output_layout = "" # "NHWC"

    # quantize options
    ptq_options = nncase.PTQTensorOptions()
    ptq_options.quant_type = "uint8"    # datatype : "float32", "int8", "int16"
    ptq_options.w_quant_type = "uint8"  # datatype : "float32", "int8", "int16"
    ptq_options.calibrate_method = "NoClip" # "Kld"
    ptq_options.finetune_weights_method = "NoFineTuneWeights"
    ptq_options.dump_quant_error = True   # False 输出模型推断的误差结果,评估转换情况
    ptq_options.dump_quant_error_symmetric_for_signed = True  #False

    # mix quantize options
    # more details in docs/MixQuant.md
    ptq_options.quant_scheme = ""
    ptq_options.quant_scheme_strict_mode = False
    ptq_options.export_quant_scheme = False
    ptq_options.export_weight_range_by_channel = False
    ############################################
    
    ptq_options.samples_count = len(calib_data[0])
    ptq_options.set_tensor_data(calib_data)
    
    print("Compiling...")
    compiler = nncase.Compiler(compile_options)
    # import
    model_content = read_model_file(model_file)
    if model_path.split(".")[-1] == "onnx":
        compiler.import_onnx(model_content, import_options)
    elif model_path.split(".")[-1] == "tflite":
        compiler.import_tflite(model_content, import_options)
    
    compiler.use_ptq(ptq_options)
    
    # compile
    compiler.compile()
    kmodel = compiler.gencode_tobytes()
    
    kmodel_path = os.path.join(dump_path, "test1.kmodel")
    with open(kmodel_path, 'wb') as f:
        f.write(kmodel)
    print("----------------end-----------------")
    return kmodel_path

模型转换

# compile kmodel single input  动态过程需要预先设置,
model_path = "./test1_dim.onnx"
dump_path = "./test1_onnx"
# sample_count is 2
calib_data = [[100*np.random.rand(1, 1).astype(np.float32), 100*np.random.rand(1, 1).astype(np.float32)]]  # 代表数据集
kmodel_path = compile_kmodel(model_path, dump_path, calib_data)

模型测试

# run kmodel(simulate)
import os

kmodel_path = "./test1_onnx/test1.kmodel"

input_data=10*np.random.rand(1, 1).astype(np.float32)
print(input_data)
result = run_kmodel(kmodel_path, input_data)
# 输出被限制在代表数据集内 代表数据集的限制
print(result)

CanMV K230开发板python(完善)

将转换后的test1.kmodel拷贝到sd卡中,可以采用读卡器读取,将文件放置在/sdcard/app/tests/k230_test/test1.kmodel位置。

链接canmv ide
新建文件并编写代码

import nncase_runtime as nn
import ulab.numpy as np

a=np.array([22])

kpu = nn.kpu()
kpu.load_kmodel("/sdcard/app/tests/k230_test/test1.kmodel")

print("inputs info:")
for i in range(kpu.inputs_size()):
    print(kpu.inputs_desc(i))

print("outputs info:")
for i in range(kpu.outputs_size()):
    print(kpu.outputs_desc(i))

input_data = np.frombuffer(a,dtype=np.float)
print(input_data.shape)
input_data = input_data.reshape((1,1))
print(input_data.shape)

kpu.set_input_tensor(0, nn.from_numpy(input_data))

# run kmodel
kpu.run()

print('input  is : ',a[0])
# get output
for i in range(kpu.outputs_size()):
    result = kpu.get_output_tensor(i)
    result = result.to_numpy()
    print('output is : ',result[0][0])

del kpu
del input_data
del result

测试结果如下图所示
k230输出结果
图中显示在结果与函数接近30×2-1=59,但300、100时其结果为192.625,当数据降为90时正常,可能的原因是在转换模型时校正数据集时,限制了模型的范围,因此校正数据集的设置很重要,虽然这是一个很小的模型,但转换后的量化导致推断出现问题,可以尝试扩大数据集的参数范围。

以上为简单的一个模型尝试过程,欢迎讨论,后续会补充一些细节及扩展,例如传感器输入数据进行推断,如adc采集或温度传感器等采集数据进行推断等。

CanMV K230开发板使用感受

python开发较为简单,便于模型的快速验证,但实现的功能有限,感觉C++的学习还是有必要的。同时工具链的使用还不熟练,官方的文档等也有待加强,嘻嘻

<think>好的,用户之前已经询问了关于如何在K230使用CanMV IDE进行异常图像检测的步骤,现在他需要的是“简单的完整代码”。我需要整合之前的代码示例,提供一个完整的、可运行的脚本。 首先,用户可能已经按照之前的步骤配置好了环境,但需要确认代码是否完整,包括模型加载、摄像头初始化、预处理、推理和后处理。需要确保代码结构清晰,关键部分有注释,方便用户理解。 接下来,我需要检查之前的代码是否有遗漏的部分。比如,之前的回答中可能没有包括完整的循环结构或异常处理。需要补全必要的部分,比如初始化传感器、加载模型、主循环中的捕获图像、预处理、推理、结果处理和显示。 另外,用户可能需要代码中的参数调整说明,比如模型路径、输入尺寸、阈值设置等。这些地方需要添加注释,提示用户根据实际情况修改。例如,模型路径是否正确,输入尺寸是否与模型匹配,阈值是否合适。 还需要考虑性能优化,比如内存释放、硬件加速等,但在简单代码中可能不需要太复杂,保持基础功能即可。不过可以添加一些常用的优化选项,如关闭自动增益和白平衡,提升帧率。 用户可能的疑问点包括模型加载失败、推理结果不正确等,所以在代码中加入错误处理或提示信息会有帮助。例如,使用try-except块捕获模型加载错误,并给出排查建议。 最后,确保代码格式正确,符合CanMV IDE的要求,使用正确的库(如KPU和sensor),并注意缩进。可能需要测试一下代码结构是否有语法错误,比如循环是否正确闭合,函数调用是否恰当。 总结来说,需要提供一个从摄像头初始化到模型加载、预处理、推理、结果显示的完整代码,附带必要注释和可调整参数,帮助用户快速上手并运行。</think>以下是为K230开发板编写的完整异常检测示例代码(包含注释说明): ```python # 硬件初始化 ======================== import sensor, image, lcd, time import KPU as kpu lcd.init(freq=15000000) # 初始化显示屏 sensor.reset() # 复位摄像头 sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # 320x240 sensor.set_vflip(1) # 根据摄像头安装方向调整 sensor.set_hmirror(1) sensor.run(1) # 启动摄像头 # 模型配置 ========================== MODEL_PATH = "/sd/anomaly_model.kmodel" # 模型需存放在SD卡根目录 INPUT_SIZE = (224, 224) # 必须与模型输入尺寸一致 THRESHOLD = 0.7 # 异常判断阈值(根据模型输出调整) try: model = kpu.load(MODEL_PATH) # 加载模型 except Exception as e: print("模型加载失败:", e) kpu.deinit(model) sys.exit() # 预处理函数 ======================== def preprocess(img): img = img.resize(INPUT_SIZE[0], INPUT_SIZE[1]) # 尺寸变换 img = img.to_grayscale() # 转灰度图(根据模型需求) img.pix_to_ai() # 转换到AI内存空间 return img # 主循环 ============================ clock = time.clock() while True: clock.tick() # 图像采集 img = sensor.snapshot() # 预处理 processed = preprocess(img) # 模型推理 kpu.forward(model, processed) # 获取输出(假设输出为[正常概率, 异常概率]) output = kpu.get_output(model, 0)[:] # 获取第0层输出 anomaly_score = output[1] # 异常置信度 # 结果显示 fps = clock.fps() lcd.display(img) # 显示原始图像 # 绘制检测信息 img.draw_string(10, 10, f"FPS:{fps:.1f}", color=(0,255,0)) if anomaly_score > THRESHOLD: img.draw_rectangle(0,0,INPUT_SIZE[0],INPUT_SIZE[1], color=(255,0,0), thickness=3) img.draw_string(10,30, f"ANOMALY: {anomaly_score*100:.1f}%", color=(255,0,0)) else: img.draw_string(10,30, f"Normal: {anomaly_score*100:.1f}%", color=(0,255,0)) # 清理资源(通常不会执行到这里) kpu.deinit(model) ``` ### 使用说明 ▼ 1. **文件准备**: - 将`.kmodel`文件重命名为`anomaly_model.kmodel` - 存入SD卡根目录 - 插入开发板卡槽 2. **硬件连接**: ```bash [摄像头] --> [K230的CSI接口] [显示屏] --> [K230的DPI接口] ``` 3. **关键参数调整**: ```python # 修改模型输入尺寸(需与训练时一致) INPUT_SIZE = (256, 256) # 示例尺寸 # 调整异常阈值(通过实际测试确定) THRESHOLD = 0.65 # 数值越小灵敏度越高 ``` 4. **运行效果**: ``` 屏幕显示实时画面,顶部显示: - FPS数值(反映处理速度) - 检测状态(Normal/ANOMALY) - 异常置信度百分比 当检测到异常时,图像周围显示红色边框 ``` ### 常见问题处理 ▼ ```python # 若出现内存不足错误,添加内存优化: import gc gc.enable() # 在主循环前启用垃圾回收 # 提升帧率技巧: sensor.set_auto_gain(False) # 关闭自动增益 sensor.set_auto_whitebal(False) # 关闭白平衡 kpu.set_layers(model, 3) # 仅运行前3层(根据模型结构调整) ``` 建议先用官方示例模型(如yolo_tiny.kmodel)验证环境配置正常,再替换为自己的异常检测模型。实际部署时需根据模型输出结构修改结果解析部分。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值