<center>
FryPi炸鸡派
STM32F411RET6开发板
</center>
简介
<font size=3>
这是一个比手掌还小的STM32F411RET6迷你开发板,核心板成本在60元以内,可以用于AI开发,UI开发,数字电源控制板等等,甚至可以拿去用于你的毕业设计和其他相关的项目。
<center>
</center>
<left> 最开始做这块板子的目的是因为上一个智能手表的项目[OV-Watch](https://github.com/No-Chicken/OV-Watch),很多复刻的人说很多器件非常难焊接,特别是主控是太小了,二次开发不太方便,再加上我也打算在STM32上部署一下AI相关的东西,做下教程,因此,FryPi炸鸡派就诞生了。这一块开发板不仅适用于初学者,也适用于进阶开发者,项目中的高级例程可能需要一定的知识储备。</left>
同时,选这个MCU的原因也是因为STM32F411REU6可以完美替代原来智能手表项目的CEU6,而且在simulink中也有F411的硬件选项。
<center>
</center>
功能特点
- MCU使用STM32F411RET6,Cortex-M4 core with DSP and FPU,512 Kbytes of Flash memory, 100 MHz CPU, ART Accelerator,.
- 可额外焊接外置SPI Flash.
- 例程丰富:例如有高级例程:[智能手表](https://github.com/No-Chicken/FryPi/tree/master/2.software/2.Advanced/6.LVGL-SmartWatch),[热成像手势识别](https://github.com/No-Chicken/FryPi/tree/master/2.software/2.Advanced/3.Thermal-camera-gesture-recognition),[手写数字识别](https://github.com/No-Chicken/FryPi/tree/master/2.software/2.Advanced/2.%20MNIST-Handwritten-number-recognition),与matlab联合开发,[simulink在环开发](https://github.com/No-Chicken/FryPi/tree/master/2.software/2.Advanced/5.simulink-Co-develope)等等.
- 留有端口可外接扩展板.(例如最上面的演示动态图,都是在Core板插上Cam扩展板)
- 有双TypeC和单TypeC版本的Core板.
- FryPi炸鸡派的pin map如下所示,目前仅有一部分IO口被用到LCD和触摸屏等外设.
<center>
</center>
硬件说明
目前的硬件工程版本为V1.1,有一个Core核心板,一个单typeC的Core核心板,一个CAM扩展板,以及一个OV2640摄像头模块,这个摄像头模块是可以在网上买的。Core板的3D示意图如下所示:
<center>
</center>
希望之后大家可以自由发挥,做一些扩展板,例如做一个传感器的扩展板,或者直接插在小车上使用。
软件说明
目前的例程demo如下目录所示,例程很丰富,分为基础例程Basic,和高级例程Advanced。详细可点击链接进入或直接在github仓库中查看。
- [Basic](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic)
- [0.template](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/0.template)
- [1.GPIO](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/1.GPIO)
- [2.USART](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/2.USART)
- [3.TIM](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/3.TIM)
- [4.PWM](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/4.PWM)
- [5.ADC](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/5.ADC)
- [6.SPI](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/6.SPI)
- [7.LCD](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/7.LCD)
- ...todo
- [Advanced](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced)
- [0.FreeRTOS模板](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/0.FreeRTOS)
- [1.如何使用CubeAI](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/1.CubeAI)
- [2.手写数字识别](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/2.MNIST-Handwritten-number-recognition)
- [3.热成像手势识别](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/3.Thermal-camera-gesture-recognition)
- [4.使用VScode EIDE插件](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/4.VScode-EIDE-build)
- [5.simulink在环开发](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/5.simulink-Co-develope)
- [6.LVGL智能手表](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/6.LVGL-SmartWatch)
- [7.OV2640摄像头+识别](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/7.OV2640-recognition)
- ...todo
上机测试
开发板焊接完毕拿到手后,烧录[模板例程](https://gitee.com/kingham/FryPi/tree/master/2.software/1.Basic/0.template)观察现象,如果硬件没有问题,那么插串口对应的TypeC连接电脑,L2会闪烁,按下Key切换模式,L2闪烁频率会改变,同时上位机会接收到模式的信息;
<center>
</center>
如果插开发板的USB对应的TypecC连接电脑,电脑会出现格式化U盘,然后格式化完后,会模拟出一个U盘。
<center>
</center>
部分例程说明
这里拿[热成像手势识别](https://gitee.com/kingham/FryPi/tree/master/2.software/2.Advanced/3.Thermal-camera-gesture-recognition)为例进行demo工程说明(其他的详细的见github):
<font color=BLACK size=4 face="黑体"> 一、文件夹组成 </font>
```python
├─python_codes
│ │ data_2_imgfile.py
│ │ data_get.py
│ │ data_show.py
│ │ gesture.h5
│ │ test.py
│ │ train.py
│ │
│ └─camera_data
│ test_data.npz
│ train_data.npz
│
└─stm32_codes
├─ThermalCamera_data_send
├─Thermalgesture
```
文件夹大致如上,`python_codes`存放的是:从下位机获取数据、网络训练、测试的代码等。采集的热成像数据保存在.npz格式的文件中,用于网络的训练和测试,模型保存为.h5格式。`stm32_codes`存放的是STM32的代码,第一个是LCD刷屏+串口发送炸鸡派获得的热成像原始数据。第二个则是已经部署好的热成像手势识别的代码。
<font color=BLACK size=4 face="黑体"> 二、热成像推流至LCD </font>
直接使用提供的MLX90640API,然后对应更改了IO口即可,同时为了让刷屏更快,在`mlx90640_display_process(void)`函数中将`drawPicture(void)`中刷屏显示使用缓存空间进行刷屏,因为打点刷屏太慢了。
<center>
</center>
<font color=BLACK size=4 face="黑体"> 三、卷积神经网络搭建、训练、部署</font>
<font color=BLACK size=4 face="黑体"> 1、CNN搭建与训练</font>
搭建的卷积神经网络如下图所示,训练集大概有4000多张图片数组数据,`num_epochs` 设置为了50,`batch_size` 设置为64,Adam优化器`learning_rate` 设置为0.01。最后Acc大概可以超过0.9。具体详细的网络搭建详见`./python_codes/train.py`。
<center>
</center> 使用keras搭建CNN模型如下所示,其他相关代码详见python代码。
```python
#------------------------------【搭模型】---------------------------------
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(filters=5, kernel_size=(5, 5), padding='valid', activation=tf.nn.relu, input_shape=(24, 32, 1)),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Conv2D(filters=3, kernel_size=(3, 3), padding='valid', activation=tf.nn.relu, input_shape=(10, 14, 5)),
tf.keras.layers.MaxPool2D(pool_size=(2, 2), padding='same'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(units=32, activation=tf.nn.relu),
tf.keras.layers.Dense(units=16, activation=tf.nn.relu),
tf.keras.layers.Dense(units=6, activation=tf.nn.softmax)
])
model.summary()
```
<font color=BLACK size=4 face="黑体"> 2、CNN部署至STM32</font>
首先将训练好得到的`gesture.h5`模型使用cubemx AI工具生成代码后,调用API,这里主要使用的`ai_mnetwork_run()`,具体如下代码块中函数`user_ai_run(const ai_float *in_data, ai_float *out_data)`所示。由于CubeMX生成代码选择的systemperform,所以要注释`MX_X_CUBE_AI_Process()`,这个里面主要是用来测试模型的性能,他默认使用的随机数填充输入。
```c
uint8_t user_ai_run(const ai_float *in_data, ai_float *out_data)
{
int idx = 0;
int batch = 0;
ai_buffer ai_input[AI_MNETWORK_IN_NUM];
ai_buffer ai_output[AI_MNETWORK_OUT_NUM];
ai_float input[1] = {0};
ai_float output[1] = {0};
if (net_exec_ctx[idx].handle == AI_HANDLE_NULL)
{
printf("E: network handle is NULL\r\n");
return -1;
}
ai_input[0] = net_exec_ctx[idx].report.inputs[0];
ai_output[0] = net_exec_ctx[idx].report.outputs[0];
ai_input[0].data = AI_HANDLE_PTR(in_data);
ai_output[0].data = AI_HANDLE_PTR(out_data);
batch = ai_mnetwork_run(net_exec_ctx[idx].handle, ai_input, ai_output);
if (batch != 1) {
aiLogErr(ai_mnetwork_get_error(net_exec_ctx[idx].handle),
"ai_mnetwork_run");
return -2;
}
return 0;
}
```
注意,这里我给到输入的数据,还是正常做了归一化的,如下代码所示。
```c
static void normalizeArray()
{
float range = maxTemp - minTemp;
for(uint16_t i=0; i<24*32; i++) {
normalizetempValues[i] = (tempValues[i] - minTemp) / range;
}
}
```
输出的数据格式为一个长度6的数组,分别对应无手势和手势1~5,类别判定条件为选择最大的数,即判哪个数最大,其对应的手势判断为当前手势,以下代码为寻找最大数对应的index。
```c
static uint8_t findMaxIndex(float arr[], uint8_t size) {
if (size <= 0) {
// 处理空数组或无效大小的情况
return -1;
}
uint8_t maxIndex = 0; // 假设最大值的索引为第一个元素的索引
for (int i = 1; i < size; ++i) {
// 检查当前元素是否大于当前最大值
if (arr[i] > arr[maxIndex]) {
maxIndex = i; // 更新最大值的索引
}
}
return maxIndex;
}
```
最后给出整个一轮识别的流程如下所示。
```c
normalizeArray();//先归一化
if(user_ai_run(normalizetempValues, outputs))//将归一化数据代入,正向运算
{
printf("\r\n run erro \r\n");
}
uint8_t temp = findMaxIndex(outputs, sizeof(outputs) / sizeof(outputs[0]));//寻找最大值的索引
printf("\r\npredict gesture:%d\r\n", temp);//打印出判断的手势
```
当然,上面只是demo中的其中一个,更多的基础和高级例程demo详见github仓库。
购买清单
1.触摸屏型号: 淘宝搜 P169H002-CTP
<center>
</center>
2.STM32F411RET6: 一般我在优信电子买
3.其他: 其他的阻容为了方便我一般就直接在立创买的
演示视频
bilibili链接: [https://www.bilibili.com/video/BV1u2421F7kf/](https://www.bilibili.com/video/BV1u2421F7kf/)
代码仓库
github: [https://github.com/No-Chicken/FryPi](https://github.com/No-Chicken/FryPi).
gitee: [https://gitee.com/kingham/FryPi](https://gitee.com/kingham/FryPi).
github上的software里面的demo工程很多我都写的比较详细,所以建议大家直接去github里面查看代码和demo教程。如果仓库没办法打开,可以在网上找一下github镜像,或者搭梯子,或者去gitee搜我。
QQ交流群
群1:572216445
群2:912218004