【Funpack4-2】 基于dsPIC33CK64MC105 Curiosity Nano和IMU传感器的数据采集、AI模型训练和轨迹识别
该项目使用了dsPIC33CK64MC105 Curiosity Nano开发板和IMU传感器,实现了训练AI模型识别IMU数据,判断不同轨迹的设计,它的主要功能为:获取IMU加速度数据,使用边缘AI模型识别轨迹。
标签
嵌入式系统
Funpack活动
EPTmachine
更新2025-07-02
江南大学
25

边缘AI应用是在MCU上运行训练好的机器学习模型,MCU采集不同的传感器数据,利用机器学习模型对其进行模式辨识。2025年,各家推出的边缘AI模型训练中,通过识别IMU六轴传感器数据来判断轨迹的类型。Micochip同样也有利用IMU数据进行轨迹识别的应用。

dsPIC33CK64MC105 Curiosity Nano是本次活动的开发板,搭载100MHz高性能数字信号控制器,配备64KB ECC Flash和8KB RAM,可以运行小型的机器学习模型。本文以dsPIC33CK64MC105 Curiosity Nano运行边缘AI模型对IMU数据进行识别,分辨轨迹的类型。

1、模型训练模型概述

边缘AI应用的开发流程一般分为数据收集、数据标注、数据训练、模型部署四个步骤。



使用Microchip的MPLAB X IDE开发边缘AI程序的流程如上图,各个阶段对应的工具如流程图中对应的描述。各个阶段完成的操作如下:

  • 数据收集阶段:在设备(开发板)上烧录用于收集数据的固件,采集不同轨迹下的IMU传感器数据;
  • 数据标注阶段:在MPLAB ML Model Builder中对采集到数据进行标注和分类,用于后续的数据训练;
  • 模型训练阶段:在MPLAB ML Model Builder中选择训练模型,对模型进行训练,根据部署的平台,编译出可以在平台上运行的库文件和示例文件;
  • 模型部署阶段:使用编译出的模型可执行库文件,创建边缘AI应用。

    具体的操作流程可以参考Microchip官方的指南,MPLAB Machine Learning Development Suite User's Guide
    下面对各个阶段的操作进行介绍

2、数据收集阶段

在dsPIC33CK64MC105 Curiosity Nano采集数据,需要用户参考示例程序编写具有相同功能的数据采集程序。Microchip官方提供有同系列的另一款芯片的采集例程可供参考。
https://github.com/microchip-pic-avr-examples/ml-dspic33ck-curiosity-imu-data-logger
在实际移植过程中发现,采用硬件I2C时,数据采样会有异常。这里采用软件模拟I2C的方式来进行替换,采用的程序是Microchip官方提供的I2C模拟驱动,其应用的平台正好是该款芯片。
https://www.microchip.com/en-us/software-library/dspic33c_i2c_softwarelibrary

在MPLAB X IDE中创建新的工程选择,选择芯片型号为dsPIC33CK64MC105。



进入工程后,点击工具栏的MCC图标,进入代码配置工具中,对MCU的外设进行设置。MCC工具启动有时会不成功,这时关闭空白的MCC窗口,重新点击MCC图标启动即可。

在左侧的工具栏中选择,修改开发的调试接口的引脚开启调试功能。


在界面上点击添加UART、GPIO、EXIT驱动库,并设置UART串口为中断传输、外部中断引脚等具体的配置信息。


完成上述配置,保存并生成代码。添加的I2C Software文件添加到工程中即可使用,使用的引脚为RB8(SCL)和RB9(SDA)。
参考imu-data-logger中的代码,将其中的部分文件和I2C驱动库复制到工程目录中并更新,修改其中不需要的部分,由于改动部分较多,可以查看附件中的工程源码。添加MPU6050的初始化函数和数据读取函数如下。


void mpu6050_sensor_init(void)
{
    uint8_t regval=0;
    uint8_t i2c_write_buffer[2];
   
    //MPU6050 Initializer
    i2c_write_buffer[0]=MPU6050_REG_PWR_MGMT_1;
    i2c_write_buffer[1]=0x80;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    DELAY_milliseconds(1000);
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
    DELAY_milliseconds(1000);
   
   
    i2c_write_buffer[0]=MPU6050_REG_PWR_MGMT_1;
    i2c_write_buffer[1]=0x01;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_PWR_MGMT_1 write\r\n");
   
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
    DELAY_milliseconds(1000);
   
    i2c_write_buffer[0]=MPU6050_REG_PWR_MGMT_2;
    i2c_write_buffer[1]=0x00;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_PWR_MGMT_2 write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
    DELAY_milliseconds(1000);
   
    i2c_write_buffer[0]=MPU6050_REG_SMPRT_DIV;
    i2c_write_buffer[1]=0x09;
    ///if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_SMPRT_DIV write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    regval=mpu6050_read(i2c_write_buffer[0]);
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_SMPRT_DIV value is %x\r\n",regval);
    }
   
    i2c_write_buffer[0]=MPU6050_REG_CONFIG;
    i2c_write_buffer[1]=0x06;
    // if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //     printf("MPU6050_REG_CONFIG write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    regval=mpu6050_read(i2c_write_buffer[0]);
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_CONFIG value is %x\r\n",regval);
    }
   
    i2c_write_buffer[0]=MPU6050_REG_GYRO_CONFIG;
    i2c_write_buffer[1]=0x18;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_GYRO_CONFIG write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    regval=mpu6050_read(i2c_write_buffer[0]);    
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_GYRO_CONFIG value is %x\r\n",regval);
    }    
   
    i2c_write_buffer[0]=MPU6050_REG_ACCEL_CONFIG;
    i2c_write_buffer[1]=0x00;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_ACCEL_CONFIG write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    regval=mpu6050_read(i2c_write_buffer[0]);
   
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_ACCEL_CONFIG value is %x\r\n",regval);
    }


    i2c_write_buffer[0]=MPU6050_REG_INT_PIN_CFG;
    i2c_write_buffer[1]=0x00;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_INT_PIN_CFG write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    regval=mpu6050_read(i2c_write_buffer[0]);
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_INT_PIN_CFG value is %x\r\n",regval);
    }
   
    i2c_write_buffer[0]=MPU6050_REG_INT_ENABLE;
    i2c_write_buffer[1]=0x01;
    //if(I2C1_Host_Write(MPU6050_ADDRESS_AD0_LOW,i2c_write_buffer,2))
    //    printf("MPU6050_REG_INT_ENABLE write\r\n");
    mpu6050_write(i2c_write_buffer[0],i2c_write_buffer[1]);
   
    regval=mpu6050_read(i2c_write_buffer[0]);    
    //if(I2C1_Host_WriteRead(MPU6050_ADDRESS_AD0_LOW,&i2c_write_buffer[0],1,&regval,1))
    {
        DELAY_milliseconds(1000);
        printf("MPU6050_REG_INT_ENABLE value is %x\r\n",regval);
    }        
   
    mpu6050status=true;
}


void MPU6050_GetData_Conti(int16_t *AccX,int16_t *AccY,int16_t *AccZ,int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ)
{
    uint8_t Data[14];
    uint8_t reg;
   
    reg=MPU6050_REG_ACCEL_XOUT_H;


    for(uint8_t i=0;i<14;i++)
    {
        Data[i]=mpu6050_read(reg);
        reg++;
    }


    *AccX=(Data[0] << 8) | Data[1];
    *AccY=(Data[2] << 8) | Data[3];
    *AccZ=(Data[4] << 8) | Data[5];
    *GyroX=(Data[8] << 8) | Data[9];
    *GyroY=(Data[10] << 8) | Data[11];
    *GyroZ=(Data[12] << 8) | Data[13];
}


程序在读取到MPU6050的数据发送到PC端时,会在数据帧的头部和尾部添加标识符,用于MPLAB Data Visualizer中进行识别。


#if DATA_VISUALIZER_BUILD
            //send out data as its read
            uint8_t headerbyte = MPDV_START_OF_FRAME;
            uint8_t *ptr = (uint8_t*)&ring_buffer[ringIdx][0];
            for(uint16_t i=0;i<rdcnt;i++)
            {
                UART1_Write(headerbyte);
               
                for(uint16_t j=0;j < sizeof(buffer_frame_t);j++)
                {
                    UART1_Write(*ptr++);
                }
               
                headerbyte = ~headerbyte;
                UART1_Write(headerbyte);
                headerbyte = ~headerbyte;
            }      
#endif




数据采集程序的移植完成并烧录到开发板后。在MPLAB X IDE中的工具栏上打开MPLAB Data Visualizer插件,也可以通过MPLAB ML Model Builder(需要登录到Microchip账户)进入MPLAB Data Visualizer,后者可以在创建模型训练工程后,将数据直接导入到云端的工程中。


在通讯配置界面设置数据采集的通讯参数和数据帧的格式。然后开始采集数据。

手持开发板沿不同轨迹运动,采集IMU数据传输到PC,


可以使用数据观察窗口的时间工具选择数据区间,并将数据保存到本地便于数据的管理。

3、数据标注阶段

数据采集完成后,需要对数据进行标注和分类,用于后续的训练和测试。在工程中添加元数据用于表示不同的轨迹类型,便于后续的数据标注。


上述采集几种轨迹的数据,对其中的数据进行分段处理,每一段的时间为60s。


标注完成后,可以查看不同类型数据的样本数量。



4、模型训练阶段

在Model Train的部分,选择“Gesture Recognition”训练模板进行后续的模型训练。根据引导设置模型的参数后,即可开始模型的训练。


训练完成后,可以查看训练完成的模型的效果。

5、模型部署阶段


在DownloadModel选项卡,可以选择模型,并编译下载响应的库文件。


解压出来的库文件包含以下内容。


参考官方的示例程序移植手册。在程序中添加以下代码,输出模型的辨识结果。

            while (rdcnt--) {
                int ret = sml_recognition_run((snsr_data_t *) ptr++, SNSR_NUM_AXES);
                ringbuffer_advance_read_index(&snsr_buffer, 1);
                if (ret >= 0) {                    
                    /* Update the voting counts */
                    votecounts[votehist[0]]--;
                    for (int i=1; i < NUM_VOTES; i++)
                        votehist[i-1] = votehist[i];
                    votehist[NUM_VOTES-1] = ret;
                    votecounts[ret]++;
                   
                    /* If there's a new state that is consistently classified as the same class, update the class ID */
                    if (ret != clsid) {
                        /* Get the class with the most votes */
                        int maxval = -1, maxcls = -1;
                        for (int i=0; i < NUM_CLASSES; i++) {
                            if (votecounts[i] > maxval) {
                                maxval = votecounts[i];
                                maxcls = i;
                            }
                        }


                        /* Only touch the LEDs if we decided on a new class */
                        if (maxval >= MAJORITY_VOTES && maxcls != clsid) {
                            clsid = maxcls;
                            if (clsid == 2) {
                                printf("Gesture classified as wave %d\n",votecounts[clsid]);
                            }
                            else if (clsid == 0) {
                            }
                            else if (clsid == 3) {                            
                                printf("Gesture classified as up & down %d \n",votecounts[clsid]);
                            }
                            else if (clsid == 4) {
                                 printf("Gesture classified as wheel %d \n",votecounts[clsid]);
                            }
                                                     
                            else if (clsid == 1) {
                                printf("Gesture classified as idle %d \n",votecounts[clsid]);
                            }
                            else {
                               
                            }
                        }
                    }      
                    kb_reset_model(0);
                }                
               
            }


模型的识别效果如下,视频演示见链接。


6、总结

本次项目中共完成两个工程,实现数据采集和边缘AI识别轨迹。感谢得捷电子和电子森林举办的Funpack活动。通过参加这次活动熟悉如何在Microchip平台上运行边缘AI应用,采集IMU传感器数据、标注数据和模型的训练、部署。作为网友们在开发边缘AI应用的一个参考。对于普通开发者而言,在开发过程中,想要效果好,需要用于训练的数据尽可能多,但是对应的模型文件也就更大。

附件下载
Funpack4_2.7z
数据采集工程、轨迹识别工程以及训练模型库文件
团队介绍
个人电子爱好者
团队成员
EPTmachine
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号