基于 Sipeed M1w Dock Suit K210 实现图像识别
该项目使用了Sipeed M1w Dock Suit K210,实现了摄像头图像信息的设计,它的主要功能为:识别图像,并解析信息,显示到 屏幕上展示。
标签
嵌入式系统
AI
悠悠兹有
更新2025-04-08
12


硬件介绍

资料

固件地址:下载站 - Sipeed

esp 8285 烧录工具:Flash 下载工具用户指南 - ESP32 - — ESP 测试工具 latest 文档

无线模块的烧录文档:更新板载 ESP32 固件 - Sipeed Wiki

AT 固件地址: Resources | Espressif Systems

openmv接口讲解:image — 机器视觉 — MicroPython 1.22 文档

sipeed接口库:标准库 - Sipeed Wiki

sipeed py工具:下载站 - Sipeed

特性

CPU : RISC-V 64bit 双核处理器,400Mhz 标准频率(可超频)

图象识别:QVGA@60FPS/VGA@30FPS

MEMS麦克风:MSM261S4030H0、灵敏度 : -26(dB,dBFS @1kHz1Pa)

音频:DAC+PA(支持 2x3W 扬声器)

Micro SD 卡插槽(TF card)

下载电路:只需要连接 USB typeC 线即可完成下载

24P DVP 和 24P MCU LCD 连接器

无线功能:支持 2.4G 802.11.b/g/n

介绍

MaixDock 开发是以M1W AI模块作为核心单元,功能非常强大。模块内置64位双核处理器芯片,拥有8M的片上SRAM,在AI机器视觉、听觉性能方面表现突出,内置多种硬件加速单元(KPU、FPU、FFT等),总算力最高可达1TOPS,可以方便地实现各类应用场景的机器视觉/听觉算法,也可以进行语言方向扫描和语言数据输出的前置处理工作。

image.png

配备2.4 英寸 LCD (分辨率:240*320)以及OV2640 鱼眼相机。

OV2640是一款由Omni Vision公司生产的1/4寸CMOS UXGA(1632x1222)图像传感器。支持整帧、子采样、缩放和取窗口等不同方式输出影像数据,其UXGA图像最高可达15帧/秒,SVGA可达30帧/秒,CIF可达60帧/秒。

开发环境搭建

下一个ide就不用其他了,下载地址: 下载站 - Sipeed

固件下载地址: 下载站 - Sipeed

烧录工具地址:github.com

固件版本从自带的6.2升级到6.3

项目框图

由m1w拍照并保存到sd卡中,取出sd卡相片后通过大语言模型。解析后的描述信息通过串口发送到m1w,并通过屏幕显示出来。

image.png


开发代码

点灯

原理图:

image.png

三个led引脚,使用io12 13 14

image.png

由灯的接线图,可知,io低电平,灯点亮。

sd卡

测试sd卡的,使用了官方例程:------- ‘capture_image_tool’。

SD卡的测试浪费了两天时间,这里要说一下,还是得看多官方疑难文档。

按照例程的readme来看,只要是spi接口的SD卡,格式是 FAT32 with MBR(msdos) partition,就能用。

对于sd卡,我的理解所有的接口都是spi,所以觉得只要卡没坏,sipeed m1w就是能够挂载sd卡的。

后来换了个卡,例程才好用了。

效果

d1e312fb905fb4476731f23e94e26401.jpg

代码

引入全局变量 board_info

from board import board_info

注册三个led,是将具体的物理引脚主导到gpio外设的某个对象上,如下图的GPIO0。类gpio可看成软件上的概念,只有和实际物理引脚对应,才能实现物理控制作用。

fm.register(board_info.LED_R, fm.fpioa.GPIO0)
fm.register(board_info.LED_G, fm.fpioa.GPIO1)
fm.register(board_info.LED_B, fm.fpioa.GPIO2)

配置gpio对象,设置为输出

led_r=GPIO(GPIO.GPIO0,GPIO.OUT)
led_g=GPIO(GPIO.GPIO1,GPIO.OUT)
led_b=GPIO(GPIO.GPIO2,GPIO.OUT)

加一个while 循环,通过变量led -r g b,就可以实现对led的控制了。

创建新的uart接口,并标记正确的引脚

from machine import UART
fm.register(15, fm.fpioa.UART2_TX, force=True)
fm.register(17, fm.fpioa.UART2_RX, force=True)
uart_A = UART(UART.UART2, 115200, 8, 0, 0, timeout=1000, read_buf_len=4096)


显示当前序号的相片并序号标记指向下一张相片

show_posi = 0
showPic = None
def show_pic():
    global show_posi
    global showPic
    os.chdir("/sd")
    dirs = os.listdir()
    images_dir = "cap_images"
    cuposi = 0
    flag = 0
    for d in dirs:
        if d[-4:] in ['.jpg', '.png', '.bmp']:
            cuposi +=1
            if cuposi == show_posi:
                flag = 1
                images_ST = "/sd/{}".format(d)
                print("get pic name is ", images_ST)
                showPic = image.Image(images_ST)
                lcd.display(showPic)
                break
    if flag == 0:
        show_posi = 0

在相片上显示信息,并自动换行

posiY = 10
def showPicWithStr(str):
    global showPic
    global posiY
    if showPic is None:
        return
    print("showPicWithStr : ",str," len:",len(str)," posiY: ",posiY)
    showPic.draw_string(30,posiY, str, color=(0,0,0), x_spacing=8,scale=1,mono_space=False,thickness =2)
    posiY +=15
    lcd.display(showPic)

计算相片名字并截取当前摄像头数据并保存到sd卡

    if takephoto == 1:
        time.sleep_ms(500)
        del img0
        img0 = sensor.snapshot()
        f_name = "{}_{}.jpg".format(images_dir, save_count)
        img0.save(f_name, quality=95)
        dirs = os.listdir()
        print(dirs);


        save_count += 1
        takephoto = 0

根据标记位的值选择显示sd卡里的相片还是实时显示摄像头的数据

    if fakg == 0:
        del img0
        img0 = sensor.snapshot()
        lcd.display(img0)
    else:
        show_posi+=1
        print("show posi pic :",show_posi)
        show_pic()
        while(True):
            if fakg == 0:
                break
            if uart_A.any():
                time.sleep_ms(10)
                jpeg_buff = uart_A.read()
                print("recv = ", jpeg_buff)
                showPicWithStr(jpeg_buff)

-------------------------------------------------------------------------------------------------------------------------------------------

保存,读取,发送数据通过串口到pc

        print("take photo!!!!!")


        #save [pic]
        img0 = sensor.snapshot()
        lcd.display(img0)
        img0.save("zsy002.jpg", quality=95)
        # read pic
        imagedata = open("zsy002.jpg",'rb').read()
       
        # send pic
        uart_A.write(b"222222")
        time.sleep_ms(2000)
        posiY = 10
        senlen = uart_A.write(imagedata)
        print("send iamge len: ",senlen)
        time.sleep_ms(1000)
        uart_A.write(b"111111")

得到串口下发的大模型解析数据,并展示到屏幕上

        takephoto =0
        jishuq =15
        while(jishuq):
            if uart_A.any():
                miaoshuStr = uart_A.read(30)
                print("image str: ",miaoshuStr)
                showPicWithStr(miaoshuStr)
            else:
                time.sleep_ms(2000)
                jishuq -= 1
        time.sleep_ms(20000)

pc上python的代码讲解

python的pc程序,是基于官方的图片解析的例程。

官方api的认证

appid = "6e00****"  #填写控制台中获取的 APPID 信息
api_secret = "ZTg0YmVkYzMwYmI2NGE3OTZ*****"   #填写控制台中获取的 APISecret 信息
api_key ="9ee9507efb642039b147c3e6*****" #填写控制台中获取的 APIKey 信息


imagedata = open("ImageUnderstanding_Python/img_1.png",'rb').read()
imageunderstanding_url = "wss://spark-api.cn-huabei-1.xf-yun.com/v2.1/image"#云端环境的服务地址
text =[{"role": "user", "content": str(base64.b64encode(imagedata), 'utf-8'), "content_type":"image"}]

打开串口

    ser = serial.Serial("COM4", 115200,timeout=0.5) 
    ser.set_buffer_size(tx_size=30)
    if ser.isOpen():                        # 判断串口是否成功打开
        print("打开串口成功。")
        print(ser.name)    # 输出串口号
    else:
        print("打开串口失败。")

得到k210拍摄的相片,并发送到大数据模型,等到大模型的描述信息返回,并通过串口发送到k210

    answer = ""
    FileJpg = None
    while True:
        com_input = ser.read(8192)
        if com_input:   # 如果读取结果非空,则输出
            if com_input[0:6] == b"111111":
                print("end image")
                if FileJpg != None:
                    FileJpg.close()
                FileJpg = None
                imagedata = open("ImageUnderstanding_Python/zsy1.jpg",'rb').read()
                text =[{"role": "user", "content": str(base64.b64encode(imagedata), 'utf-8'), "content_type":"image"}]
                question = checklen(getText("user","最简单的用英语描述一下,不超过200字"))
                main(appid, api_key, api_secret, imageunderstanding_url, question)
                getText("assistant", answer)
                ser.write( answer.encode())
                continue
            if com_input[0:6] == b"222222":
                print("start image")
                if FileJpg != None:
                    FileJpg.close()
                FileJpg= open('ImageUnderstanding_Python/zsy1.jpg', 'wb+')
                continue
            print(len(com_input))
            if FileJpg != None:
                FileJpg.write(com_input)

效果展示

image.png

image.png

疑难杂症

image.png

无线模块不可使用,以上日志看得出来只有一级boot,8285模块固件烧录缺失,需要重新烧录固件。


烧录串口指令失败,返回的都是乱码。

通过烧录工具,能看到晶振是0,看其他人的晶振都是有数据的,不确定是不是芯片错误,导致指令执行返回数据错误。

image.png

image.png

跳转用户程序后,无法运行,失败了,再次芯片重启运行,无限循环重启。

image.png

一个月了,受不了了,我觉得我的板卡里的8285是坏掉的。

不管他了,烧录程序已经把我逼疯了。

---------------------------------------------------------------------------

关于显示的颠倒问题,开发过程中出现了图片和字体显示方向颠倒的问题。图像正的,字体是倒着的。

查看接口draw_string的参数,由位置,间距和字符串,但是没有对于显示颠倒的解释。

后来测试发现,字体不能调正,它是固定按照xy坐标,正的方向现实的,所以那个显示颠倒是因为图像倒了。


图像的显示分为两个部分:sensor的方向,lcd的方向。

这里就设计到两个接口:

sensor.set_vflip(1)  摄像头垂直翻转。
lcd.rotation(0)		显示的屏幕旋转几个90度​

我的问题是摄像头拍的反了,垂直倒过来就好了。

如下

image.png


创建uart2 乱码

发了一串字符,只能得到前几个字符是正确的,后面的字符都是错误的,如下

发了一大串1.

image.png

只有几个字符是正确的

image.png

原因

打开的uart模式不一致,这里犯了经验错误了,直接使用默认配置读写了。

接口初始化说明

image.png

和这里保持一致即可

image.png

---------------------------

例程有讲,在字符串出现 \n时,会自动换行,可验证例程:

import lcd, time
import image
lcd.init(freq=15000000)
img = image.Image()
# img.draw_rectangle((0,0,240,240), fill=True, color=(30,30,30))
str = b'hello \nmaixpy'
img.draw_string(60, 100, str, scale=4)
lcd.display(img)


while True:
  time.sleep_ms(10) # ohter event

验证确实有用,但是加载汉字字库‘0xA00000_font_uincode_16_16_tblr.Dzk’后

换行的转义就会失效,不知道问题出现在哪里,有机会研究研究。

文档

大模型的讲解 :Maix Bit(K210)保姆级入门上手教程---外设基本使用_maixbit-CSDN博客

官方Wiki: Board - Sipeed Wiki

结语

电子森林的文本编辑器支持 ctrl+s保存,这点真好用。

算是第二次测试这类ai开发板子,有点陌生,有点无从下手,不过每天参与一点,慢慢就能入门,还算大概了解一点。

看多了,学多了,也就会了,希望电子森林活动越来越好。





附件下载
0xA00000_font_uincode_16_16_tblr.Dzk
new code.zip
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号