打印
[STM32F4]

神经网络移植到STM32

[复制链接]
666|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
将神经网络移植到STM32

最近在做的一个项目需要用到网络进行拟合,并且将拟合得到的结果用作控制,就在想能不能直接在单片机上做神经网络计算,这样就可以实时计算,不依赖于上位机。所以要解决的主要是两个问题,一个是神经网络的移植,另一个是STM32的计算速度。
神经网络的移植

网络采用的是最简单的BP神经网络,基本原理可以自己去了解一下,大概就是通过若干次矩阵运算
A X + B AX+B AX+B
将m个输入对应到n个输出。一般地,矩阵运算之后会跟上一个激活函数(我不知道是不是叫这个名字),常见的有sigmoid和tansig等等。了解了这个之后就是移植了。
网络移植大致可以分为三步:训练网络,网络参数提取,最后是在KEIL中将预测的代码写出来,下面结合例子和代码进行说明

使用特权

评论回复
评论
为你转身 2022-11-15 23:01 回复TA
———————————————— 版权声明:本文为CSDN博主「苏小泉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_37953670/article/details/120587487 
沙发
为你转身|  楼主 | 2022-11-15 23:02 | 只看该作者
网络的训练

训练网络具体的过程就不说了,我是在matlab中进行的,训练完之后可以得到一个网络net。我这里输入是12个变量,输出是1个变量,三层网络,神经元个数分别为50,50和20。

使用特权

评论回复
板凳
为你转身|  楼主 | 2022-11-15 23:03 | 只看该作者
网络参数提取

网络参数提取也是在matlab中进行的,代码如下
load NET3 %网络
clear temp
%% 网络的参数,不过网络结构,{}里面的索引不一样
w{1}=net.IW{1};
w{2}= net.LW{2};
w{3}=net.LW{3,2};
w{4}=net.LW{4,3};
b=net.b;

%%归一化参数
[row,col] = find(net.inputConnect==1);   %获取输入矩阵
ps_Xxmax = net.inputs{row,col}.range(:,2);
ps_Xxmin = net.inputs{row,col}.range(:,1);
ps_Xymax = net.inputs{row,col}.processedRange(:,2);
ps_Xymin = net.inputs{row,col}.processedRange(:,1);
[row,col] = find(net.outputConnect==1);   %获取输入矩阵
ps_Yxmax = net.outputs{row,col}.range(:,2);
ps_Yxmin = net.outputs{row,col}.range(:,1);
ps_Yymax = net.outputs{row,col}.processedRange(:,2);
ps_Yymin = net.outputs{row,col}.processedRange(:,1);
%% 测试输入变量
dataX=[19513.4489795918,20577.612244898,20159.6326530612,20345.1020408163,19241.9387755102,19875.1428571429,17836.8163265306,18450.1734693878,19108.2142857143,17741.193877551,20197.5,17988.5
]';
%% 开始计算
temp{1} = (dataX-ps_Xxmin)./(ps_Xxmax-ps_Xxmin).*(ps_Xymax-ps_Xymin)+ ps_Xymin;   %输入归一化
%% 矩阵计算和激活函数计算
for i=2:4
    temp{i} = tansig_apply( w{i-1}*temp{i-1}+b{i-1} );    % 前numLayers-1循环计算
end
x = w{4}*temp{4}+b{4}         % 最后一层不使用tansig函数
dataY = (ps_Yxmax-ps_Yxmin).*(x-ps_Yymin)./(ps_Yymax-ps_Yymin)+ps_Yxmin   %反归一化
%%最后是将相应变量输出到txt,为了方便写入KEIL中
for i=1:length(w)
    d=w{i};
    d=d';
    writematrix(d(:)',['w' num2str(i)]);
end

for i=1:length(b)
    d=b{i};
    writematrix(d(:)',['b' num2str(i)]);
end
writematrix((ps_Xxmax)','ps_Xxmax');
writematrix((ps_Xxmin)','ps_Xxmin');
writematrix((ps_Xymax)','ps_Xymax');
writematrix((ps_Xymin)','ps_Xymin');

writematrix((ps_Yxmax)','ps_Yxmax');
  writematrix((ps_Yxmin)','ps_Yxmin');
writematrix((ps_Yymax)','ps_Yymax');
writematrix((ps_Yymin)','ps_Yymin');

function a = tansig_apply(n,~)      %tansig函数,为了能够编译成C
    a = 2 ./ (1 + exp(-2*n)) - 1;
end

使用特权

评论回复
地板
为你转身|  楼主 | 2022-11-15 23:03 | 只看该作者
测试输入变量对应的输出如下

使用特权

评论回复
5
为你转身|  楼主 | 2022-11-15 23:04 | 只看该作者
移植到KEIL

下一步就是将上面的matlab代码移植到KEIL中,这个应该不难,因为没有复杂的算法,不过需要借助一下矩阵计算的库
移植分为两步,第一步是将网络参数写入,第二步是实现计算过程。因为网络参数太多,这里就不放上来了,有兴趣的可以下载源文件看。这里只把实现计算过程的代码放上来。

使用特权

评论回复
6
为你转身|  楼主 | 2022-11-15 23:05 | 只看该作者
float32_t Get_Hm(float32_t input[12])
{
            
                u8 i=0;
//        float32_t tempinput[12];
//        memcpy(tempinput,input,4*12);
        //归一化
        for(i=0;i<12;i++)
        {
                //float32_t t=input[i]-ps_X_xmin[i];
                input[i]=(input[i]-ps_X_xmin[i])/(ps_X_xmax[i]-ps_X_xmin[i])*(ps_X_ymax[i]-ps_X_ymin[i])+ ps_X_ymin[i];
                //printf("%d: %5f\r\n",i,t);
        }
//                T_C_data[0]=T;
//                T_C_data[1]=C;
//        //temp{1} = (dataX-ps_X.xmin)./(ps_X.xmax-ps_X.xmin).*(ps_X.ymax-ps_X.ymin)+ ps_X.ymin;   //输入归一化
//                T_C_data[0]=(T-ps_X_xmin[0])/(ps_X_xmax[0]-ps_X_xmin[0])*(ps_X_ymax[0]-ps_X_ymin[0])+ ps_X_ymin[0];
//                T_C_data[1]=(C-ps_X_xmin[1])/(ps_X_xmax[1]-ps_X_xmin[1])*(ps_X_ymax[1]-ps_X_ymin[1])+ ps_X_ymin[1];
                //输入层次
          arm_mat_mult_f32(&W1,&InputM,&h1);
       
        for(i=0;i<50;i++)
        {
                //printf("%d: %5f\r\n",i,h_data1[i]);
                h_data1[i]=h_data1[i]+B1_data[i];
        }         
         for(i=0;i<50;i++)
         {
                h_data1[i]=2/(1+exp(-2*(h_data1[i])))-1;  //tansig函数
         }
                 //隐藏层
                 
     arm_mat_mult_f32(&W2,&h1,&h2);
                 for(i=0;i<50;i++)
     {
      h_data2[i]=2/(1+exp(-2*(h_data2[i]+B2_data[i])))-1;  //tansig函数
     }
                 arm_mat_mult_f32(&W3,&h2,&h3);
                 for(i=0;i<20;i++)
     {
      h_data3[i]=2/(1+exp(-2*(h_data3[i]+B3_data[i])))-1;  //tansig函数
     }
                 arm_mat_mult_f32(&W4,&h3,&OutputM);
                 
//                 Hm_data=2/(1+exp(-2*(Hm_data+B2_data[0])))-1;  //tansig函数
//                 //输出层
                 
     Outputdata=Outputdata+B4_data[0];
                 //反归一化
                 Outputdata = (ps_Y_xmax[0]-ps_Y_xmin[0])*(Outputdata-ps_Y_ymin[0])/(ps_Y_ymax[0]-ps_Y_ymin[0])+ps_Y_xmin[0];   

                 //Hm_data=(Hm_data*(0.9555-0.1055)+0.1055)*100;   
                          
           return  Outputdata;
}

使用特权

评论回复
7
为你转身|  楼主 | 2022-11-15 23:06 | 只看该作者
结果验证

再移植完后,关心的问题就是算得对不对以及计算速度。我在循环里面一直进行计算,并通过串口发出,这样基本可以确定计算需要花费的时间。

使用特权

评论回复
8
为你转身|  楼主 | 2022-11-15 23:06 | 只看该作者
和matlab计算结果对比,可以看到是基本一致的,计算速度的话通过时间戳可以看到大概是0.03-0.05s算一次,也就是说基本可以保证20hz左右的频率,对于我这个项目来说是可以用的。

STM32的文件可以通过网盘下载。
链接:https://pan.baidu.com/s/1vKwwk3UdTDvNR6McFNVmCQ
提取码:tysa

使用特权

评论回复
9
jf101| | 2024-6-23 15:20 | 只看该作者
神经网络具体能处理哪些事情和应用呢

使用特权

评论回复
10
万图| | 2024-11-16 07:12 | 只看该作者

含有延展到远高于基本开关频率的谐波

使用特权

评论回复
11
Uriah| | 2024-11-16 08:15 | 只看该作者

这些引线越窄越好

使用特权

评论回复
12
帛灿灿| | 2024-11-16 10:11 | 只看该作者

输入电容主要是起到高频能量存储器的作用

使用特权

评论回复
13
Bblythe| | 2024-11-16 11:14 | 只看该作者

输入滤波电容的公共端应作为其他交流电流地的唯一接点

使用特权

评论回复
14
周半梅| | 2024-11-16 13:10 | 只看该作者

它们对PCB布局的重要性

使用特权

评论回复
15
Pulitzer| | 2024-11-16 14:13 | 只看该作者

混淆的话,会引起电源工作不稳定

使用特权

评论回复
16
童雨竹| | 2024-11-16 16:09 | 只看该作者

把这干扰信号再次辐射出去

使用特权

评论回复
17
Wordsworth| | 2024-11-16 17:12 | 只看该作者

对变换器效率测量

使用特权

评论回复
18
Clyde011| | 2024-11-16 18:15 | 只看该作者

把纹波电流分摊到每个电容上

使用特权

评论回复
19
公羊子丹| | 2024-11-16 19:08 | 只看该作者

印制电路板(PCB)的线路设计

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

681

帖子

0

粉丝