1. 程式人生 > >BP神經網路在Stm32微控制器上的應用

BP神經網路在Stm32微控制器上的應用

概述 硬體上使用STM32F4+MPU9150實現的神經網路識別手勢,不過沒有用IMU的地磁資料,只用了三軸加速度計和三軸陀螺儀的資料,板子是自己畫的主要參照了意法官方的開發板的原理圖(人生畫的第一個板子還沒有錯誤哈,容小逗比高興一下。。。)。MPU9150的驅動是用的InvenSense提供的eMPL硬體抽象層,雖然這個driver配置imu內建DMP比較方便,但感覺這個bias矯正和姿態解算做的並不是很好,而且原始碼沒公開不好改。不過識別部分用的是原始資料沒用融合出的姿態資料,(姿態用在另一個功能上了)。考慮到微控制器的計算效能不高(其實是訓練部分不好移植^_^)於是把網路的訓練部分放在在matlab上做的,然後把訓練完的網路的閾值和權值匯出來,放到微控制器裡。這裡涉及到了微控制器採集的資料怎麼發給matlab的問題,幸好高版本的matlab對硬體的支援有大幅提升,可以通過串列埠來收資料。網路在微控制器上的識別過程計算量還是挺大的,原始的imu資料(6Dof)經過一個巴特沃斯低通濾波器後放到一個類似於FIFO的資料結構中,從這個FIFO中首先進行間隔取數(間隔根據手勢動作時間計算),並對取出的數進行歸一化,然後將這些資料傳給網路進行識別。過程中的濾波、歸一化和網路計算都要進行大量的浮點運算,於是把logsig函式由泰勒展開改成了查表,還開了FPU,用了CMSIS-DSP。

Matlab串列埠接收 下面是建立串列埠obj的指令碼

try
    try
        obj=serial('COM15','baudrate',115200,'parity','none','databits',8,'stopbits',1);
        flag_fetch=1;
    catch
        fprintf('Create Obj Error');
    end
    obj.BytesAvailableFcnMode = 'terminator';
    obj.Terminator = 'c';
    obj.BytesAvailableFcn 
[email protected]
_nn_callback;     try         fopen(obj);     catch         fprintf('Open Error\n');         break;     end     pause;     flag_fetch=0; catch     fprintf('Serial Read Error!\n'); end fclose(obj); delete(obj); clear obj;

下位機將imu資料以資料幀的形式傳送上來,資料幀自定義協議,幀以字元’c’結尾,這裡將串列埠配置為terminator模式,設定terminator=’c’,這樣該模式下接收到字元’c’就呼叫一次回撥函式”serial_nn_callback”

下面是回撥函式的框架:

function [c]= serial_nn_callback(obj, ~)

% var start
...
% var end

try
    n = get(obj, 'BytesAvailable');
    if n>20&& (flag_fetch==1)
        a = fread(obj, 29, 'uchar');%資料幀長度
        if a(1)~='A'
            fscanf(obj) ;
        end
        % 協議解析 start
        % 協議解析 end
    end
    catch
end

這裡讀取之後要首先判斷資料幀頭是否吻合,如果不吻合立即呼叫 fscanf(obj) 清除串列埠的快取,否則的話接收到的資料都是串的。這樣就可以成功的接收到下位機發送的資料。

網路訓練 這個部分比較簡單,用的單S啟用函式,mse能到的最小值有限,後來試了下雙S啟用函式,mse可以很小。不過實際測試時單S啟用函式的識別率已經夠用了

input=input';
net = newff( minmax(input) , [mid_layer,output_layer] , { 'logsig' 'logsig' }  ,'traingdx') ; 
net.trainparam.show = 50 ;
net.trainparam.epochs = 500 ;
net.trainparam.goal = 0.00001 ;
net.trainParam.lr = 0.001 ;

網路匯出

w_i2l=net.IW{1,1};%輸入層到中間層的權值
b_i2l=net.b{1,1};%輸入層到中間層的閾值
w_l2o=net.LW{2,1};%中間層到輸出層的權值
b_l2o=net.b{2,1};%中間層到輸出層的閾值
% 匯出函式 start
...
% 匯出函式 end

微控制器上網路計算函式

void layer_to_layer(
    int lin_num,
    int lout_num,
    float32_t* input,
    float32_t* output,
    float32_t* w,
    float32_t* b 
    )
{
     float32_t *temp;
     memset(output, 0, 
                lout_num * sizeof(float32_t)); 
     temp = (float32_t *)malloc(lin_num * sizeof(float32_t));
     for (int i=0;i<lout_num;i++)
     {
#ifdef    ARM_M4        
        arm_mult_f32(input,w+i*lin_num,temp,lin_num);    //CMSIS-DSP庫 向量相乘
#else
                for (int i=0;i<lin_num;i++)                      //非DSP指令
                {
                  *(temp+i)=*(input)*(*(w+i*lin_num))
                }         
#endif 
        for (int j=0;j<lin_num;j++)
        {
            *(output+i)=*(output+i)+*(temp+j);
        }
        *(output+i)=*(output+i)+*(b+i);
        *(output+i)=logsig_fun(*(output+i));
     }
     free(temp);
}

CMSIS-DSP庫上的說明和例子都很詳細 ---------------------  作者:BosonInLHC  來源:CSDN  原文:https://blog.csdn.net/u011070641/article/details/50491826  版權宣告:本文為博主原創文章,轉載請附上博文連結!