单片机_简单AI模型训练与部署__从0到0.9
IDE: CLion
MCU: STM32F407VET6
一、导向
以求知为导向,从问题到寻求问题解决的方法,以兴趣驱动学习。
虽从0,但不到1,剩下的那一小步将由你迈出。本篇主要目的是体验完整的一次简单AI模型部署流程,从数据采集到模型创建与训练,再到部署单片机上。选定的训练方向也非常简单,既不是手势识别、语音识别等这些较为复杂的模型,也不是使用一些第三方的预训练模型,而是简简单单地“调包”搭建一个判断输入数字大小的小模型,比如在判断输入数据是否小于24。最终生成的C代码其内存、存储占用均为10KB,也可缩减至不到1KB
既然是“从零开始”,那么就不需要介绍太多复杂的术语解释,但一些基本的概念还是需要了解的。该篇最主要的目的就是体验,不需要知道太多为什么,真正上手实践后,再自行学习。
此处所指的单片机是STM32系列(stm32f407vet6)
二、流程
1,STM32CubeMX的AI插件
想要触碰一个未曾熟知的领域,最重要的是要搜集信息,了解要干什么、怎么干,然后简单体验一番。那么第一步来了,我们的问题很简单,stm32单片机上怎么跑AI。带着这个问题我们使用搜索引擎可以得到一些博客,什么模型搭建、部署什么的可能也听不大懂。
但从这些博客里我们可以找到一个共同点,那就是都使用到了STM32CubeMX,虽然AI模型相关的不太懂,但这个工具软件可太熟了。
从这里我们可以获取另一个关键点,就目前所获取的信息来看stm32单片机上跑AI应是依赖STM32CubeMX的AI插件的。至于怎么配置这个AI插件,相关的博客有很多
2,AI模型
当你兴冲冲地使用CubeMX上的AI插件时,你可能会发现,缺少一件东西——AI模型
回想到前面回答中,通义(通义千问)说过导入模型
虽然网上相关博客有不少,什么手势识别、神经网络算法等等,但几乎没有几个是直接给你一个AI模型的,要么是到官网的github上找,要么是什么云盘。总之对于初学者而言,是有一些麻烦的
接下来继续发挥“不会就问”的精神
通义给了我们四个方案,但无论是官方给的,还是用什么第三方的,亦或者使用预训练模型的,总之都是有一些难度的。四种方案你可以逐一尝试,尝试下来后,你可能会发现预训练、官方模型库、第三方库这些方案太难了,因为涉及到大量陌生知识。
自己训练模型看着比较可行,因为从下面可以看到,自己训练一个简单模型就两步,收集数据和python编程来训练模型。无论是收集数据,还是编程,似乎都是非常清晰的过程。
当然,上面的结论也是问出来的。当通义给你一个问题的回答,而你对其中的一些概念又含混不清的时候,你可以把自己的猜测反问给通义,且不必担心通义出言不逊。
反问后,无论你的猜测是否正确,你最终都会被指导一个正确的方向。
既然确定了步骤,不妨问问更细节的一些东西,这里需要自行提问,下面提供一个简单的示例
3,数据采集
前面说过,既然是初学者,那么一切从简,怎么简单怎么来。这里先说一个前置信息,AI模型训练需要的数据集,一般是保存在.csv文件里的,打开后你会发现这跟Excel表格大差不差。
使用记事本打开后,你会发现头部会有一些标签,数据是按照列来排布的
左边那一列是输入的数据,右边那一列是输出的结果,当然也可以称为类别
训练AI模型时,会用你收集到的这个数据集训练,以图中这个数据为例(二分类问题),给它左边的输入数据,让AI模型输出,然后与数据集中的右边的输出对比,来判断AI预测的结果,之后AI再不断调整权重、参数什么的,让预测更加精准。
这些前置信息,你自己也是可以收集的。这里,我们是需要一个数据集来帮我们训练,也知道数据集长成什么样子。从一些博客或者AI我们可以了解到,一般的数据集都是什么图像相关的,什么矩阵、像素、灰度之类的,那显然还是有些难度的。
所以接下来,我们讨论更简单的情况,就是给AI一个数据,让它判断是不是小于一个数,小于就输出1,大于就输出0。是不是简单很多了?
4、训练模型
我们首先要清楚,STM32上跑的这个AI模型其实是神经网络模型,它们之间的关系是这样的:
AI(人工智能)> 机器学习 > 深度学习 > 神经网络模型(非传统神经网络)
总之经过一系列问询之后,我们可以选择Keras 和TensorFlow Lite。这是一个神经网络框架,不要害怕陌生的术语名称,它只是用于训练AI的工具(你也可以理解为库或包)。
这里我补充一点就是,STM32CubeMX其实对Keras的支持其实并不算好,我们后续真正使用的是后者。
三、采集数据
正如前面所言,为了简单体验这个流程,我们就以那个判断数字是否小于某个数为训练目的。当体验过这个简单流程后,你可以放飞自我去训练了。
这里不说怎么安装Pycharm什么的,因为这是最基本的能力。
现在我们把训练目的具体化:在0-100内,判断出这个数据是否小于24。那么我们就需要生成这样的数据,为了保真,还得让数据随机起来,且小于24的和大于24的概率还不同,以增加些许难度。
这个数据可以使用python生成,那么怎么写python脚本呢?从变量命名、标识符开始学一遍python?那是大可不必的,不需要掌握python(因为你至少已掌握了C语言),只需要知道怎么让通义生成正确的代码就行了
通义生成的代码不一定可用,你把报错信息或者调试信息给它,提供给它需求,让它不断更新代码直至生成可用的代码。这里不展开细节了,下面就是可用的python代码,如果你是新安装的pycharm,那么可能会提示安装一些库,安装软件包的过程可能会有些漫长,这都是正常现象。有时间了可以自行查资料解决
import numpy as np import pandas as pd# 参数设置 filename = 'simulated_data.csv' num_points = 100000 threshold = 24 # 触发阈值 low_value_frequency = 0.33 # 低于24的值的概率# 生成数据 np.random.seed(0) data = np.random.randint(0, 101, num_points) # 生成0到100的整型数据# 生成低于24的值 low_values = np.random.binomial(1, low_value_frequency, num_points) data[low_values == 1] = np.random.randint(0, 24, np.sum(low_values))# 标记是否触发阈值 labels = (data < threshold).astype(int)# 创建DataFrame df = pd.DataFrame({'value': data, 'label': labels})# 保存到CSV文件 df.to_csv(filename, index=False)print(f"数据已生成并保存到 {filename}")
正确执行完后,当前目录下就会有一个simulated_data.csv文件。
四、训练模型
虽然听着不明觉厉,其实这里我们只用非常简单有限的步骤,因为框架已经帮我们做好了绝大部分工作了。
问询你可以描述得更加具体
总之,多次让AI改进后,可以得到了一份可以训练刚才数据的代码
import pandas as pd import numpy as np import matplotlib.pyplot as plt from keras.src.callbacks import ModelCheckpoint from keras.src.layers import GRU from sklearn.model_selection import train_test_split from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense from tensorflow.keras.models import save_model import tensorflow as tf import subprocess# 数据加载 df = pd.read_csv('simulated_data.csv') X = df['value'].values.reshape(-1, 1) # 特征值需要reshape为2D数组 y = df['label'].values# 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 创建模型 model = Sequential([Dense(10, input_shape=(1,), activation='relu'), # 输入层(同时也是隐藏层)# Dense(1, activation='sigmoid') # 输出层# GRU(16, input_shape=(1, 1), return_sequences=False), # 添加GRU层,32个单元,输入形状为 (1, 1),不返回序列Dense(18, activation='relu'), # 添加一个全连接层,32个神经元,使用ReLU激活函数Dense(1, activation='sigmoid') # 添加全连接层,输出1个节点,使用sigmoid激活函数 ])# 编译模型 model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])# 设置 ModelCheckpoint 回调函数 checkpoint = ModelCheckpoint('best_model.keras', monitor='val_accuracy', save_best_only=True, mode='max')# 训练模型 history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=78, batch_size=60)# 绘制训练过程中的损失 plt.figure(figsize=(12, 6)) plt.plot(history.history['loss'], label='Training Loss') plt.plot(history.history['val_loss'], label='Validation Loss') plt.title('Model Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()# 绘制训练过程中的准确率 plt.figure(figsize=(12, 6)) plt.plot(history.history['accuracy'], label='Training Accuracy') plt.plot(history.history['val_accuracy'], label='Validation Accuracy') plt.title('Model Accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()# 模型保存 save_model(model, 'model/stm32_model.keras') # 将 .keras 模型转换为 .tflite 格式 converter = tf.lite.TFLiteConverter.from_keras_model(model)# # 使用 Select TF Ops,虽然允许把Keras转为TFLite模型,但是CubeMX的AI插件不支持 # converter.target_spec.supported_ops = [ # tf.lite.OpsSet.TFLITE_BUILTINS, # 支持 TFLite 内置操作 # tf.lite.OpsSet.SELECT_TF_OPS # 支持 TensorFlow 原生操作 # ] # # 禁用实验性降低张量列表操作 # converter._experimental_lower_tensor_list_ops = Falsetflite_model = converter.convert()# 保存 TFLite 模型 with open('model/stm32_model.tflite', 'wb') as f:f.write(tflite_model) print("TFLite 模型已保存")# # 下面出了问题 # # 加载最佳模型 # best_model = tf.keras.models.load_model('best_model.keras') # # # 保存最佳模型 # save_model(best_model, 'model/stm32_best_model.keras') # # # 将 .keras 模型转换为 .tflite 格式 # # 转换模型 # tflite_model = converter.convert() # # # 保存 TFLite 模型 # with open('model/stm32_best_model.tflite', 'wb') as f: # f.write(tflite_model) # # print("TFLite best模型已保存")
这个代码,即便你安装了所有软件包可能还会报错,这都是正常现象,可以不用管它
训练模型这一步,步骤非常清晰明了
①加载数据
②创建模型
这里面可以看到三个函数,也就是三层神经网络,你想要增加,就再添加一个函数即可。只不过经过我的测试和官方文档说明,无法使用更加复杂的层,比如GRU层(陌生术语稍微过一下就行,暂时可以不必深究)
③编译模型
回调函数先不用管它,可加可不加
④训练模型
这里只要注意这两个参数即可,一个是epochs,另一个是batch_size。前者是训练轮数,后者是每批次训练的数据量
⑤评估模型
这里其实就是图形化显示训练的结果,什么准确率、损失什么的
⑥保存模型
这里可以看到,一开始模型格式为.keras,后面就编程了.tflite。原因就是前者格式STM32CubeMX的AI插件经常无法正常加载,报一些奇奇怪怪的错。
既然这样,我们看看运行之后是什么样子的
如果这是你的第一个AI模型,成就感满满了不是
右边的第二张图(还有一张在小窗口里),可以看到随着训练轮次增加,准确率也逐渐增加
我们单看某一次的,可以发现正确率已经达到0.9988了,因为问题比较简单嘛。有时会达到1.00
五、部署推理
既然模型已经训练完毕,接下来我们就可以在本地部署,然后进行推理看看效果怎么样。(加载训练好的模型,然后输入数据,看看AI的输出是什么样的)
不过在此之前我们还要生成100个模拟数据,当做实际中的数据,用于验证模型的推理效果。
import numpy as np import pandas as pd# 参数设置 filename = 'simulated_data1.csv' num_points = 100 threshold = 24 # 触发阈值 low_value_frequency = 0.33 # 低于24的值的概率# 生成数据 np.random.seed(0) data = np.random.randint(0, 101, num_points) # 生成0到100的整型数据# 生成低于24的值 low_values = np.random.binomial(1, low_value_frequency, num_points) data[low_values == 1] = np.random.randint(0, 24, np.sum(low_values))# 标记是否触发阈值 labels = (data < threshold).astype(int)# 创建DataFrame df = pd.DataFrame({'value': data, 'label': labels})# 保存到CSV文件 df.to_csv(filename, index=False)print(f"数据已生成并保存到 {filename}")
然后就是推理(预测)了
import numpy as np from tensorflow.keras.models import load_model import pandas as pd import matplotlib.pyplot as plt# 设置 matplotlib 使用的字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号# 加载模型 model = load_model('model/stm32_model.keras')# 加载验证数据集 df = pd.read_csv('simulated_data1.csv') X_test = df['value'].values.reshape(-1, 1, 1) y_test = df['label'].values# 进行推理 predictions = model.predict(X_test) predicted_labels = (predictions > 0.5).astype(int)# 输出详细结果 for i, value in enumerate(df['value']):print(f"输入数据: {value:.2f}, 预测结果: {predicted_labels[i][0]}, 实际标签: {y_test[i]}")# 计算准确率 accuracy = np.mean(predicted_labels.squeeze() == y_test) print(f"模型准确率: {accuracy * 100:.2f}%")# 可视化预测结果和真实标签的对比 plt.figure(figsize=(12, 6)) plt.plot(y_test, label='真实标签', marker='o') plt.plot(predicted_labels.squeeze(), label='预测结果', marker='x') plt.title('真实标签 vs 预测结果') plt.xlabel('样本索引') plt.ylabel('标签') plt.legend() plt.show()
我们可以看到推理后的结果,这根本就难不倒它嘛
六、加载模型
既然前面已经把模型训练了出来,接下来就可以把模型部署到单片机上了。下面先介绍一些分析模型时的问题
遇到这个提示后
- 在注册表编辑器中,找到路径
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem
。修改或添加
LongPathsEnabled
值:
- 在
FileSystem
文件夹中查找名为LongPathsEnabled
的DWORD (32-bit) 值。- 如果存在,双击它并将其值设置为
1
。- 如果不存在,右键点击
FileSystem
文件夹,选择新建
->DWORD (32-bit) 值
,命名为LongPathsEnabled
,然后将其值设置为1
。- 然后重启
发生下面这个问题,比较奇葩,虽然提示说我们引入了batch_shape,但实际上我们没有显式引用。但只要把模型转为TFLite格式,同时不能直接使用GRU等高级层。
即便……你使用Select TF Ops,虽然可以转为TFLite模型,但是Cube的AI插件不能转换
# 模型保存
save_model(model, 'model/stm32_model.keras')
# 将 .keras 模型转换为 .tflite 格式
converter = tf.lite.TFLiteConverter.from_keras_model(model)
# 使用 Select TF Ops
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS, # 支持 TFLite 内置操作tf.lite.OpsSet.SELECT_TF_OPS # 支持 TensorFlow 原生操作
]# 禁用实验性降低张量列表操作
converter._experimental_lower_tensor_list_ops = False
tflite_model = converter.convert()
每次加载模型都要重新分析一遍
1,生成C代码
①添加AI插件
这一步有太多博客介绍了,我就说一点,把下面那个Device也勾选上,选择最后一个应用模板即可。官方文档里也很详细
②添加模型
③分析模型
点击分析即可,分析成功后应如下
我们可以看到最后一句,由于前面训练模型时也是这样,输入和输出只有一个,这里输入和输出也是一个4字节(实际为float32)
如果你想要知道模型的详细情况,可以选择这个选项,当然这都是后面了,可以自己尝试
④生成的文件
生成的模型代码就是这个文件夹了
以及Middlewares下面的这个AI库
2,移植库
如果你移植很熟练的话,这一步也没什么难度。不过要说明的是,我的工程是cmake工程,移动文件后,只需要修改CMakelists就行了。如果是Keil或者IAR的话,需要手动图形化添加。
不管是哪种,目的都一样,先把官方提供的AI库(是整个AI目录而不仅仅是Lib目录)添加到工程里,就是下面这个静态库.a
在CMakelists里就是这样,前面是什么不重要,重要的是这个库前面要加上“:”,为什么呢?我从CubeMX生成工程的CMakelists里扒出来的就是这样(疑惑的话可以多问问通义等)
target_link_libraries(libai INTERFACE :NetworkRuntime910_CM4_GCC.a) target_link_libraries(libai PRIVATE libdrivers)
移植后,这个库的头文件也要包含进来,ST/AI/Inc
然后是这个X-CUBE-AI,具体过程我不描述了,移植需要多尝试、去练,讲求的是经验
总之,就两个东西,一个是官方的库,另一个是资源文件
3,使用
前面选择软件包时,由于勾选了应用模板,所以会给我们生成。这些文件中,我们只要考虑这两个文件即可
从这个头文件里可以看到,它提供了两个接口,一个是初始化,另一个是AI处理。
到资源文件中,我们可以看到这两个函数是空的,我们先补充初始化函数,直接这样添加就行了,不用管什么错误判断,因为实际上这个ai_boostrap接口就已经做好了
void MX_X_CUBE_AI_Init(void) {ai_boostrap(data_activations0); }
至于函数处理,我们可以不用void MX_X_CUBE_AI_Process(void);
我们自己编写一个简单的接口,输入数据,然后返回数据。为什么前面要加上float强制转换呢?因为这个指针类型其实就是void*
float process_data_float(float input) {*(float *)data_ins[0]=input;ai_run();return *(float *)data_outs[0]; }
从这个函数可以清晰地看出,把接收的数据存放进data_ins[0]指向的缓冲区,然后调用ai_run进行AI推理,之后返回data_outs[0]得到推理的结果。无论是data_ins[0]还是data_outs[0]其实都是指针,所以前面要用“*”把指针指向的缓冲区的值取出来或者修改。
接着,我们在某个按键处理中调用这个函数,输入的是从0累加到150的数据供它验证
input = 0;for (int i = 0; i < 500; ++i){result = process_data_float(input);printf("index:%f result:%f\r\n", input, result);input += 0.3;}
按下按键后,串口打印的结果也符合推理结果,小于24的为1,大于24的为0。在24附近出现波动的原因也很简单,因为我提供的数据集里并没有出现浮点数,全是0-100的整数,并且模型训练轮次和数据量都比较小。
Tips:
这三行代码我想了好久才想出来,前面定义data_ins和data_outs时不是有个int8_t,这个东西把我误导了许久,问通义,它说什么标准化、偏移量、量化之类的,总之告诉我输入数据和输出数据就是一个字节(int8_t)。后来我使用CubeMX另几个选项生成模板,并查看官方手册
最终才确定转换的模型输入数据和输出数据确实是float32而不是量化后的int8_t,然后大胆尝试,强制把ai_input[0].data(也就是data_ins[0])转换为float32,才得到正确的结果
七、跨越
AI模型转为C代码可以不使用CubeMX的这个插件,但那样占用可能会很高,对于STM32平台,无论是操作的便捷性还是针对STM32的性能优化,都是使用官方的比较合适。如果是其他单片机,那么直接使用由TensorFlow Lite训练的模型转换的C代码,占用也不会很高,因为它专门用于嵌入式平台。
至于神经网络、CNN、LVTM、图像识别什么的,从0.9到∞,需要靠自己主动学习了。
相关文章:

单片机_简单AI模型训练与部署__从0到0.9
IDE: CLion MCU: STM32F407VET6 一、导向 以求知为导向,从问题到寻求问题解决的方法,以兴趣驱动学习。 虽从0,但不到1,剩下的那一小步将由你迈出。本篇主要目的是体验完整的一次简单AI模型部署流程&#x…...

对撞双指针(七)三数之和
15. 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组…...

【Ubuntu24.04】服务部署(虚拟机)
目录 0 背景1 安装虚拟机1.1 下载虚拟机软件1.2 安装虚拟机软件1.2 安装虚拟电脑 2 配置虚拟机2.1 配置虚拟机网络及运行初始化脚本2.2 配置服务运行环境2.2.1 安装并配置JDK172.2.2 安装并配置MySQL8.42.2.3 安装并配置Redis 3 部署服务4 总结 0 背景 你的服务部署在了你的计算…...

timm库加载的模型可视化
在深度学习中,模型的可视化有助于了解模型的结构和层级关系。以下是几种方式来可视化使用 timm 库加载的模型: 打印模型结构 torch.nn.Module 的子类(包括 timm 的模型)可以通过 print() 查看其结构:import timm# 加…...

服务限流、降级、熔断-SpringCloud
本文所使用的组件:Nacos(服务中心和注册中心)、OpenFeign(服务调用)、Sentinel(限流、降级)、Hystrix(熔断) 项目结构: service-provider:提供服…...

2024最新YT-DLP使用demo网页端渲染
2024最新YT-DLP使用demo网页端渲染 前提摘要1.使用python的fastapi库和jinjia2库进行前端渲染2.代码实现1)目录结构2)代码style.cssindex.htmlresult.htmlmain.pyrun.py 3)运行测试命令端运行 3.项目下载地址 前提摘要 2024最新python使用yt…...

《第十部分》1.STM32之通信接口《精讲》之IIC通信---介绍
经过近一周的USART学习,我深刻体会到通信对单片机的重要性。它就像人类的手脚和大脑,只有掌握了通信技术,单片机才能与外界交互,展现出丰富多彩的功能,变得更加强大和实用。 单片机最基础的“语言”是二进制。可惜&am…...

wireshark使用lua解析自定义协议
wireshark解析自定义协议 1.自定义的lua放入路径2.修改init.lua2.1 开启lua2.2 init.lua文件最后加入自己的lua文件位置,这里需要确保与自己的文件名相同 3.编写lua4.编写c抓包5.wireshark添加自定义协议如何加调试信息 1.自定义的lua放入路径 一般是自己软件的安装…...

(Keil)MDK-ARM各种优化选项详细说明、实际应用及拓展内容
参考 MDK-ARM各种优化选项详细说明、实际应用及拓展内容 本文围绕MDK-ARM优化选项,以及相关拓展知识(微库、实际应用、调试)进行讲述,希望对你今后开发项目有所帮助。 1 总述 我们所指的优化,主要两方面: 1.代码大小(Size) 2.代码性能(运行时间) 在MDK-ARM中,优…...

Qt实现可拖拽的矩形
之前项目上需要用Qt来绘制可拖拽改变形状的矩形。看了Qt Graphics相关的内容,虽然对Qt怎么添加图元的有了些了解,但是具体如何实现拖拽效果,一时也没有什么好的想法。还好网上有人分享的例子,很受启发。后来又回顾了一下这部分的代…...

CentOS:A服务器主动给B服务器推送(上传),B服务器下载A服务器文件(下载)
Linux:常识(bash: ip command not found )_bash: ip: command not found-CSDN博客 rsync 中断后先判断程序是否自动重连:ps aux | grep rsync 查看目录/文件是否被使用(查询线程占用):lsof /usr/local/bin/mongodump/.B_database1.6uRCTp 场景:MongoDB中集合非常大需要…...

Oracle 执行计划查看方法汇总及优劣对比
在 Oracle 数据库中,查看执行计划是优化 SQL 语句性能的重要工具。以下是几种常用的查看执行计划的方法及其优劣比较: 1. 使用 EXPLAIN PLAN FOR 和 DBMS_XPLAN.DISPLAY 方法 执行 EXPLAIN PLAN FOR 语句: EXPLAIN PLAN FOR SELECT * FROM …...

TCL大数据面试题及参考答案
Mysql 索引失效的场景 对索引列进行运算或使用函数:当在索引列上进行数学运算、函数操作等,索引可能失效。例如,在存储年龄的列上建立了索引,若查询语句是 “SELECT * FROM table WHERE age + 1 = 20”,这里对索引列 age 进行了加法运算,数据库会放弃使用索引而进行全表扫…...

九、FOC原理详解
1、FOC简介 FOC(field-oriented control)为磁场定向控制,又称为矢量控制(vectorcontrol),是目前无刷直流电机(BLDC)和永磁同步电机(PMSM)高效控制的最佳选择…...

vue页面成绩案例(for渲染表格/删除/添加/统计总分/平均分/不及格显红色/输入内容去首尾空格trim/输入内容转数字number)
1.使用v-if 和v-else 完成<tbody>标签的条件渲染 2.v-for完成列表渲染 3.:class完成分数标红的条件控制 删哪个就传哪个的id,基于这个id去过滤掉相同id的项,把剩下的项返回 a标签的默认点击事件会跳转 这里要禁止默认事件 即使用click.provent 就…...

STM32编程小工具FlyMcu和STLINK Utility 《通俗易懂》破解
FlyMcu FlyMcu 模拟仿真软件是一款用于 STM32 芯片 ISP 串口烧录程序的专用工具,免费,且较为非常容易下手,好用便捷。 注意:STM32 芯片的 ISP 下载,只能使用串口1(USART1),对应的串口…...

Centos使用docker搭建Graylog日志平台
日志管理系统有很多,比如ELK,Graylog,LokiGrafanaPromtail 适用场景: 1.如果需求复杂,服务器资源不受限制,推荐使用ELK(Logstash Elasticsearch Kibana)方案; 2.如果需求仅是将…...

自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例
Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析:…...

【SQL】【数据库】语句翻译例题
SQL自然语言到SQL翻译知识点 以下是将自然语言转化为SQL语句的所有相关知识点,分门别类详细列出,并结合技巧说明。 1. 数据库操作 创建数据库 自然语言:创建一个名为“TestDB”的数据库。 CREATE DATABASE TestDB;技巧:识别**“创…...

linux基本命令2
7. 文件查找和搜索 (继续) find — 查找文件 find /path/to/search -name "file_name" # 根据名称查找文件 find /path/to/search -type f # 查找所有普通文件 find /path/to/search -type d # 查找所有目录 find /path/to/search -name "*.txt" # 查找…...

Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
Redisson 是一个高性能的 Java Redis 客户端,提供了丰富的分布式工具集,如分布式锁、Map、Queue 等,帮助开发者简化 Redis 的操作。在集成 Redisson 到项目时,开发者通常有两种选择: 使用 Redisson 原始依赖。使用 Re…...

Git命令使用与原理详解
1.仓库 # 在当前目录新建一个Git代码库 $ git init # 新建一个目录,将其初始化为Git代码库 $ git init [project-name] # 下载一个项目和它的整个代码历史 $ git clone [url]2.配置 # 显示当前的Git配置 $ git config --list # 编辑Git配置文件 $ git co…...

Linux:自定义Shell
本文旨在通过自己完成一个简单的Shell来帮助理解命令行Shell这个程序。 目录 一、输出“提示” 二、获取输入 三、切割字符串 四、执行指令 1.子进程替换 2.内建指令 一、输出“提示” 这个项目基于虚拟机Ubuntu22.04.5实现。 打开终端界面如图所示。 其中。 之前&#x…...

vue项目中中怎么获取环境变量
在 Vue 项目中,有几种获取环境变量的方法。最常用的是通过 import.meta.env 来访问。 1.首先在项目根目录创建环境变量文件: .env # 所有环境都会加载 .env.development # 开发环境 .env.production # 生产环境2.在环境变量文件…...

C#里怎么样使用正则表达式?
C#里怎么样使用正则表达式? 正则表达式是由普通字符(如英文字母)以及特殊字符(也称为元字符)组成的一种文字模式 这种文字模式可用于检查字符串的值是否满足一定的规则,例如: 验证输入的邮箱是否合法 输入的身份证号码是否合法 输入的用户名是否满足条件等 也可以…...

《生成式 AI》课程 第5講:訓練不了人工智慧?你可以訓練你自己 (下)
资料来自李宏毅老师《生成式 AI》课程,如有侵权请通知下线 Introduction to Generative AI 2024 Springhttps://speech.ee.ntu.edu.tw/~hylee/genai/2024-spring.php 摘要 这一系列的作业是为 2024 年春季的《生成式 AI》课程设计的,共包含十个作业。…...

Vue 动态给 data 添加新属性深度解析:问题、原理与解决方案
在 Vue 中,动态地向 data 中添加新的属性是一个常见的需求,但它也可能引发一些问题,尤其是关于 响应式更新 和 数据绑定 的问题。Vue 的响应式系统通过 getter 和 setter 来追踪和更新数据,但 动态添加新属性 时,Vue 并不会自动为这些新属性创建响应式链接。 1. 直接向 V…...

【Pytest+Yaml+Allure】实现接口自动化测试框架
一、框架思想 requestsyamlpytestallure实现接口自动化框架。结合数据驱动和分层思想,将代码与数据分离,易维护,易上手。使用yaml编写编写测试用例,利用requests库发送请求,使用pytest管理用例,allure生成…...

el-input绑定点击回车事件意外触发页面刷新
小伙伴们在项目中应该还是比较常用键盘指定按键事件的,尤其是一些筛选条件的通过点击键盘回车按键去触发搜索 例如: <el-form><el-form-item label条件title><el-input v-modelformData.searchKey keydown.entersearch></el-input…...

Golang的语言特性与鸭子类型
Golang的语言特性与鸭子类型 前言 什么是鸭子类型? Suppose you see a bird walking around in a farm yard. This bird has no label that says ‘duck’. But the bird certainly looks like a duck. Also, he goes to the pond and you notice that he swims l…...