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 版權宣告:本文為博主原創文章,轉載請附上博文連結!