FastBond2阶段2-适用于MAX78000FTHR的拓展板_数字识别
MAX78000FTHR是一款基于MAX78000微控制器的开发板。MAX78000是美信半导体公司推出的一款低功耗神经网络处理器,专为在边缘设备上部署人工智能(AI)应用而设计的,此拓展板外接2.4寸SPI串口TFT显示屏,使用电池供电。
标签
嵌入式系统
开发板
PCB设计
FastBond第二季
HAN_
更新2023-08-29
590

一、前言:

   在FastBond项目的第一阶段,在Scheme-it软件中绘制了详细的框图和原理图。这个过程包括了对整个系统的设计思路的梳理,以及各个模块之间的连接关系的确定。同时,也进行了物料的选型,确保选用的物料能够满足项目的性能需求和成本控制。接下来在kicad软件中完成了PCB的绘制工作和打板工作。FastBond项目的第二阶段。在这个阶段中,会继续完善设计,做出实物模型,并进行效果演示。

二、原理图与PCB设计:

   在FastBond项目的第一阶段,已经进行了PCB的绘制和打板,但是在后续进行焊接测试的时候出现了一些问题。

  1. 原设计使用的REG1117-5是一款州仪器(Texas Instruments)生产的降压线性稳压器,而我使用的是3.7V的锂电池进行供电,通过设计的电路发现降压到了2.7V,根本达不到MAX78000FTHR供电要求。
  2. 原设计的MAX78000FTHR的排母插座两端在PCB布线的时候大了100mil,两端的尺寸出现了100mil的偏差,导致不兼容。使用面包板测量的时候为9个孔的宽度,我误认为900mil,事实上需要800mil。

FlSiDWDoqebhNiUHKzQ8VcMqcDF1FlHBYKXMnO_oRasX1KUKC7m774Rw

由图可以看到刚刚好差了一点点,为了解决这些问题,我对于物料进行了重新选型和重新打板,优化了布局,预留出锂电池的的位置。

  1. 排母插座两端宽度更改为800mil。
  2. 使用德州仪器(Texas Instruments)生产的TPS61070DDCR DC-CD电源芯片,重新设计电路。

   为了符合MAX78000FTHR的接口,放置了1x16 1x12的排母插座,将宽度修改为800mil

FpoXWzVSfrnBhNsO-lMDC7AUJWIU

 

   改用TPS61070DDCR芯片来设计新的电路系统。这个电路的主要任务是对锂电池进行升压处理,以便能够提供足够的电力供应给MAX78000FTHR。通过这种方式,确保MAX78000FTHR能够稳定且高效地运行,而不会出现电力不足的问题。

   TPS61070DDCR是德州仪器(Texas Instruments)推出的一款高效节能的升压直流-直流(DC-DC)转换器芯片。该芯片采用了降低功耗和提高效率的先进技术,适用于各种电池供电应用。具有 600mA 开关的 90% 高效同步升压转换器。

   主要特性:

  1. 90% 高效同步升压转换器 
  2. 器件静态电流:19 µA(典型值)
  3. 输入电压范围:0.9V 至 5.5V
  4. 可调输出电压高达 5.5 V
  5. 可提供省电模式版本,以改善低输出功率时的效率
  6. 停机期间负载断开
  7. 过温保护
  8. SOT-23-6封装

应用:

  1. 所有一节、两节和三节碱性、镍镉或镍氢电池供电型产品或单节锂电池供电型产品 】
  2. 便携式音频播放器
  3. PDA
  4. 蜂窝电话
  5. 个人医疗产品
  6. 白光 LED 照明

FgnY6WSIB2AgJRoxEc2192B8WsRP

Fio0Qf8M-7I8dDcWTC4cehtx06us

 

三、程序设计:

   MAX78000FTHR程序开发主要有两个部分,神经网络部署和外设开发。

神经网络部署流程:

FmWKFiuohVYtQ6b71qAgcSOKSIwm

   神经网络部署流程中需要注意的是,官方所提供的ai8x-training和ai8x-synthesis中完整展示了训练 量化 评估模型 生成C代码的过程,但是并没有说明如何搭建神经网络模型,需要根据MAX78000的模型网络限制进行构建神经网络模型,也就是说并不是所以的模型都可以在MAX78000上进行部署的。建议参考官方模型进行修改搭建模型,以达到更好的效果。

   因为我的笔记本电脑是集显的,所以我整个过程是在服务器上完成的。配置为PyTorch  1.8.1/Python  3.8(ubuntu18.04)/Cuda  11.1。由于我的能力有限并且是第一次接触此类,所以大部分都是参考官方。

神经网络部署:

1:收集数据制作训练集和测试文件

      首先先http://yann.lecun.com/exdb/mnist/index.html上下载训练集和数据集以及其对应标签,建立MNIST文件夹,将其解压放入MNIST文件夹中。并且对训练集和测试集的加载和预处理操作。

import torchvision
from torchvision import transforms
import ai8x

def my_mnist_data(data, load_train=True, load_test=True):

    (data_dir, args) = data

    if load_train:
        train_transform = transforms.Compose([
            transforms.RandomCrop(28, padding=4),
            transforms.RandomAffine(degrees=20, translate=(0.1, 0.1), shear=5),
            transforms.ToTensor(),
            ai8x.normalize(args=args)
        ])

        train_dataset = torchvision.datasets.MNIST(root=data_dir, train=True, download=True,
                                                   transform=train_transform)
    else:
        train_dataset = None

    if load_test:
        test_transform = transforms.Compose([
            transforms.ToTensor(),
            ai8x.normalize(args=args)
        ])

        test_dataset = torchvision.datasets.MNIST(root=data_dir, train=False, download=True,
                                                  transform=test_transform)

        if args.truncate_testset:
            test_dataset.data = test_dataset.data[:1]
    else:
        test_dataset = None

    return train_dataset, test_dataset


datasets = [
    {
        'name': 'MNIST',
        'input': (1, 28, 28),
        'output': list(map(str, range(10))),
        'loader': my_mnist_data,
    },
]

2:模型搭建

   模型搭建主要是参考官方的ai85net5模型。

from torch import nn

import ai8x


class AI85Net5(nn.Module):
    """
    5-Layer CNN that uses max parameters in AI84
    """
    def __init__(self, num_classes=10, num_channels=3, dimensions=(28, 28),
                 planes=60, pool=2, fc_inputs=12, bias=False, **kwargs):
        super().__init__()

        # Limits
        assert planes + num_channels <= ai8x.dev.WEIGHT_INPUTS
        assert planes + fc_inputs <= ai8x.dev.WEIGHT_DEPTH-1

        # Keep track of image dimensions so one constructor works for all image sizes
        dim = dimensions[0]

        self.conv1 = ai8x.FusedConv2dReLU(num_channels, planes, 3,
                                          padding=1, bias=bias, **kwargs)
        # padding 1 -> no change in dimensions -> MNIST: 28x28 | CIFAR: 32x32

        pad = 2 if dim == 28 else 1
        self.conv2 = ai8x.FusedMaxPoolConv2dReLU(planes, planes, 3, pool_size=2, pool_stride=2,
                                                 padding=pad, bias=bias, **kwargs)
        dim //= 2  # pooling, padding 0 -> MNIST: 14x14 | CIFAR: 16x16
        if pad == 2:
            dim += 2  # MNIST: padding 2 -> 16x16 | CIFAR: padding 1 -> 16x16

        self.conv3 = ai8x.FusedMaxPoolConv2dReLU(planes, 128-planes-fc_inputs, 3,
                                                 pool_size=2, pool_stride=2, padding=1,
                                                 bias=bias, **kwargs)
        dim //= 2  # pooling, padding 0 -> 8x8
        # padding 1 -> no change in dimensions

        self.conv4 = ai8x.FusedAvgPoolConv2dReLU(128-planes-fc_inputs,
                                                 fc_inputs, 3,
                                                 pool_size=pool, pool_stride=2, padding=1,
                                                 bias=bias, **kwargs)
        dim //= pool  # pooling, padding 0 -> 4x4
        # padding 1 -> no change in dimensions

        self.fc = ai8x.Linear(fc_inputs*dim*dim, num_classes, bias=True, wide=True, **kwargs)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

    def forward(self, x):  # pylint: disable=arguments-differ
        """Forward prop"""
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x


def ai85net5(pretrained=False, **kwargs):
    """
    Constructs a AI85Net5 model.
    """
    assert not pretrained
    return AI85Net5(**kwargs)



models = [
    {
        'name': 'ai85net5',
        'min_input': 1,
        'dim': 2,
    },
]

3:训练

训练主要是依靠官方的train.py 文件。

python train.py --lr 0.1 --optimizer SGD --epochs 200 --deterministic --compress policies/schedule.yaml --qat-policy qat_policy.yaml --model ai85net5 --dataset MNIST  --param-hist --device MAX78000 --pr-curves 

其中qat_policy.yaml 和 schedule.yaml都为官方提供的文件。

其中train.py的各参数含义如下:

  • --lr:设置初始学习率
  • --optimizer SGD:优化器选择为随机梯度下降(Stochastic Gradient Descent),用于更新模型的参数。
  • --epochs:训练次数
  • --deterministic:固定值的随机数生成器添加种子
  • --compress:设置压缩和学习率调度
  • --qat-policy:在YAML文件中定义QAT策略(默认:policies/qat_policy.yaml)。
  • --model:训练模型
  • --dataset:数据集加载目录
  • --param-hist:收集参数统计信息
  • --device:运行设备
  • --pr-curves:生成精确召回曲线

训练后会在logs文件夹中生成以下文件

FiKT2UH_mTbBSL7zhU6MqXxDl7aZ

4:量化

注意要更改为为自己的路径。

python quantize.py my_train/qat_best.pth.tar my_train/my_mnist.pth.tar --device MAX78000 -v -c mnist.yaml

5:模型评估

模型评估也是依赖于train.py 

python train.py --model ai85net5 --dataset MNIST --confusion --evaluate --exp-load-weights-from ../ai8x-synthesis/my_train/my_mnist.pth.tar -8 --device MAX78000

6:生成C代码

python ai8xize.py --verbose --test-dir MY_Project --prefix ai85-mnist --checkpoint-file my_train/my_mnist.pth.tar --config-file networks/mnist-chw-ai85.yaml --compact-data --softmax --device MAX78000 

生成C代码主要依赖于ai8xize.py

其中ai8xize.py的各参数含义如下:

  • --verbose:在运行过程中输出详细的日志信息
  • --test-dir:指定生成工程存储路径
  • --prefix:设置测试名前缀
  • --checkpoint-file:包含量化权重的检查点文件,量化后生成的文件
  • --config-flie:包含层配置的YAML配置文件
  • --compact-data:输入数据方式
  • --softmax:将softmax函数添加到生成的代码中
  • --device:运行设备

之后会生成以下文件

FndbQ4Bit9HwJhrJFxcgIvv1Kj83

其中主要的是cnn.h/weights.h/cnn.c/softmax.c。

外设开发:

   在参考官方代码进行外设开发的时候,发现官方的代码应用层耦合度特别高,为了兼容不同的开发板,同一个功能在不同例程中有不同方式的实现,而且每一个例程风格都不太一样,变量定义/函数命名/注释等都特别难受,但是代码又能看得懂,一种很奇妙的平衡,我从Tools中复制了一份模板,按照自己的习惯添加文件和编写代码。

FgCAryW3xWGvVxM4jfeOBkmhB6Tk

主要流程:

Ft_fKpID-4uFylO5gHczGIlAK-6H

主要函数如下:

void App_Camera_Cnn_Get(void)
{
    uint8_t *frame_buffer;
    uint8_t *buffer;
    uint32_t imgLen;
    uint32_t w, h, x, y;
    uint8_t r, g, b;
    uint32_t color;
    int cnt = 0;
    
    camera_start_capture_image();

    while (!camera_is_image_rcv())
    {
    }

    camera_get_image(&frame_buffer, &imgLen, &w, &h);
    buffer = frame_buffer;

    for (y = 0; y < 112; y++)
    {
        for (x = 0; x < 112; x++)
        {
            r = *buffer++;
            g = *buffer++;
            b = *buffer++;
            buffer++; // skip msb=0x00
            // change the range from [0,255] to [-128,127] and store in buffer for CNN           
            input_0[cnt++] = ((b << 16) | (g << 8) | r) ^ 0x00808080;        

            color = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);

            MXC_TFT_WritePixel(x * 3, y * 3, 3, 3, color);

        }
    }
}

   mian函数主要实现外设初始化过程,CNN推理过程和串口打印推理情况

int main(void)
{
    MXC_Delay(200000);
    int i;
    int digs, tens;

    MXC_ICC_Enable(MXC_ICC0); // Enable cache

    // Switch to 100 MHz clock
    MXC_SYS_Clock_Select(MXC_SYS_CLOCK_IPO);
    SystemCoreClockUpdate();

    // DO NOT DELETE THIS LINE:
    MXC_Delay(SEC(2)); // Let debugger interrupt if needed
    App_Tft_Init();
    App_Camera_Init();

    App_Tft_Print(buff, 20, 213, font_library, snprintf(buff, sizeof(buff), "My_Project"));
    MXC_Delay(1000000);
    MXC_TFT_ShowImage(0, 0, image_bitmap_1);
    MXC_Delay(1000000);
    MXC_TFT_ClearScreen();

    // Enable peripheral, enable CNN interrupt, turn on CNN clock
    // CNN clock: APB (50 MHz) div 1
    cnn_enable(MXC_S_GCR_PCLKDIV_CNNCLKSEL_PCLK, MXC_S_GCR_PCLKDIV_CNNCLKDIV_DIV1);
    cnn_boost_enable(MXC_GPIO2, MXC_GPIO_PIN_5);
    cnn_init();         // Bring state machine into consistent state
    cnn_load_weights(); // Load kernels
    cnn_load_bias();
    cnn_configure(); // Configure state machine
    MXC_SYS_ClockEnable(MXC_SYS_PERIPH_CLOCK_CNN);

    while (1)
    {
        App_Camera_Cnn_Get();
        load_input(); // Load data input
        cnn_start();  // Start CNN processing

        SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; // SLEEPDEEP=0
        while (cnn_time == 0)
            __WFI(); // Wait for CNN

        /*         if (check_output() != CNN_OK)
                    fail(); */
        softmax_layer();

        /*         cnn_disable(); // Shut down CNN clock, disable peripheral */

        for (i = 0; i < CNN_NUM_OUTPUTS; i++)
        {
            digs = (1000 * ml_softmax[i] + 0x4000) >> 15;
            tens = digs % 10;
            digs = digs / 10;
            printf("[%7d] -> Class %d: %d.%d%%\n", ml_data[i], i, digs, tens);
        }
        asciiart((uint8_t *)input_0);
        MXC_Delay(500000);
    }
}

四、遇到的问题及解决方法:

1:PCB绘制错误和元器件选型错误

      重新打板,选择新器件进行原理图绘制和PCB打板

2:识别效果差

      输入模型应该为28*28的uint_32位数据,而摄像头捕获的数据如果直接为28*28的话,在TFT上根本看不清楚摄像头的拍摄情况,所以我选择了通过摄像头获取112*112然后再经过图形变换转换为28*28

FjX7BWCbNI1T3NZ1-KAXw4NkSO92

、功能展示图:

FgbHahGfpNIgJSCaHE-2jmtSABLqFoejn3uWCFV0zM0X4buM_EsFic8Y

FjpvaPdY7gbDIHrZrsWM_11NdAH0FnTWvWX1HcOZfHZD5Yk3CmqHuy_t

六:总结

   MAX78000是一款性能优秀的芯片,之前只是接触过传统类的MCU,MAX78000的学习过程让我了解了人工智能相关的东西,并且学习了在Linux上通过敲指令的方式的开发MAX78000的流程,但是本人能力有限大部分参考官方实现,详细代码可以查看附件,而且数字识别的效果也一般,容易受到光照等因素的影响,作为初学者我建议参考官方的模型搭建,避免自己搭建的模型在MAX78000上的识别效果差,我这次学习依然有很多不足之处和疑惑之处,后面还需要时间进行开发优化,同样感谢硬禾团队的此次活动。

物料清单
附件下载
Project_10.rar
完善后的Kicad工程
MY_MNIST-main.rar
代码工程
Project_9.pdf
原理图PDF文件
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号